--- vhook/pip.c.orig 2006-10-23 17:50:27.000000000 +0300 +++ vhook/pip.c 2006-10-23 18:00:53.000000000 +0300 @@ -0,0 +1,1040 @@ +/* + * Picture In Picture (PiP) Hook + * Version 1.0 + * Copyright (c) 2006 Michael Stoyanov screamer (at) data (dot) bg + * Based on watermark.c (2005) by Marcus Engene myfirstname(at)mylastname.se + * + * + * Options to PiP: + * -m nbr = nbr is 0 or 1. 0 is the default mode, see below. + * -t nbr = nbr is threshold (six digit hex) + * -c nbr = nbr is contrast percent over the original pixels, see below. + * -s nbr = nbr is the frame on which the PiP will start + * -e nbr = nbr is the frame on which the PiP will stop + * -d nbr = nbr is the number of frames used for fade in & fade out process + * -x nbr = nbr is the x coord on which the PiP drawing will start + * -y nbr = nbr is the y coord on which the PiP drawing will start + * -w nbr = nbr is the desired width of the PiP + * -h nbr = nbr is the desired height of the PiP + * -f file = file is the filename of the PiP image/video. You must specify this! + * + * + * Working with MODE 0: + * Let's assume that the threshold is the default one - 0x80 hex (128 dec) + * Per color (for each pixel) do this: + * If the PiP color is exactly 0x80, no change to the original color. + * If the PiP color is > 0x80 the new color is equal to the original color + the + * abs difference between the PiP color and the threshold. + * If the PiP color is < 0x80 the new color is equal to the original color - the + * abs difference between the PiP color and the threshold. + * If result is greater than 0xff (255), result will be 0xff (255). Same for 0 + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif' outfile + * + * Working with MODE 1: + * If all PiP colors (for each pixel) > threshold color, the PiP pixel will + * replace the original pixel. + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -m 1' outfile + * + * Working with MODE 2: + * The PiP picture is applied over the movie source without any transperancy. + * A.k.a. "pure PiP" + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -m 2' outfile + * + * + * Options -x, -y, -w & -h. PiP positioning (usable in all modes): + * The default behaviour of PiP is to stretch the PiP source to the size of the + * main source and apply the selected mode (0, 1 or 2). + * By using the -x & -y options you can specify the x,y coordinate at + * which the PiP source drawing will start. With -w & -h you can specify the + * desired PiP source width, height. + * Let's assume that you want to draw an image (or video) in the bottom-right + * part of a movie source with dimensions 608x336 pixels. + * You want the PiP to have dimensions 150x130 pixel and to position it's top-left + * corner on coordinates x400/y180 over the movie source (that's the bottom-right part) + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -x 400 -y 180 -w 150 -h 130' outfile + * Use the different modes (0, 1 or 2) to make the PiP transparent, partially + * transparent or fully applied over the main source + * Example mode 1: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -x 400 -y 180 -w 150 -h 130 -m 1' outfile + * Example mode 2: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -x 400 -y 180 -w 150 -h 130 -m 2' outfile + * + * Option -t. Threshold (usable in all modes): + * You can change the threshold (default 0x80) with the -t flag. + * If threshold is 000000 the color values of picture is added to destination. + * This way in MODE 0, a mask that is visible both in light pictures and in + * dark can be made (fex by using a picture generated by gimp and the bump map tool). + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -t 222222' outfile + * + * Options -s & -d. PiP duration (usable in all modes): + * With -s, -d switches you can specify the start and the end of the PiP effects + * using frame duration. + * Let's assume we have movie source with 25fps. + * The example will start the PiP process on 50th frame (-s 50) of the movie + * source (2nd second) and will stop it on the 250th frame (-e 250), or 10th + * second. + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -s 50 -e 250' outfile + * + * Option -d. Fade In/Fade Out effects (usable only in MODE 0 with -s and -e): + * With -d switch you can make TV-like fade-in/fade-out effects. + * The PiP will make the same thing as using -s and -e (see PiP duration) with + * the only difference that it will produce a fade in and fade out effect with + * duration 25 frames (or 1 second) + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -s 50 -e 250 -d 25' outfile + * + * Option -c. Contrast mask (usable only in MODE 0): + * In mode 0 the pixel calculation formula without contrast is: + * new_pixel_color = orig_pixel_color + (pip_image_color - threshold) + * It is applied to Red, Green, Blue channels of the pixel. + * When using the -c option the formula changes to: + * new_pixel_color = orig_pixel_color + (pip_image_color - threshold) + * + ((orig_pixel_color - threshold) * contrast_percent) + * In that way you have control over the difference between the movie source + * (a.k.a "orig") pixel color and the threshold (or contrast) + * If using the default threshold (without -t) the -c option is similar to + * applying contrast ONLY over that PiP pixels that aren't equal to the + * threashold (like contast-mask). + * When using positive values for -c, the dark pixels of the movie source will + * become even more dark, the light ones will become even more light. + * When using negative values for -c, the dark pixels of the movie source will + * become more light, the light ones will become even more dark. + * Note that the -c switch is in percents (-100 to 100) + * Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.gif -c -20' outfile + * + * Complex Example: ffmpeg -i infile -vhook '/path/pip.so -f wm.avi -x 400 -y 180 -w 150 -h 130 -s 50 -e 500 -d 25 -m 0 -c -20' outfile + * Meaning (on 25dps video): Draw a PiP with dimensions 150x130 pixels, using + * mode 2, starting on coordinates 400x180 (of the main source), start drawing + * it at frame 50 (2nd second), stop drawing it at frame 500 (20th second), + * use fade-in & fade-out effects with duration 25 frames for the process + * (1 second) and add contrast mask -20% + * + * An example watermark file is at + * http://engene.se/ffmpeg_watermark.gif + * + * + * Note that the entire vhook argument is encapsulated in ''. This + * way, arguments to the vhook won't be mixed up with those to ffmpeg. + * + * 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 + */ + +#include +//#include +#include +#include + +#include "common.h" +#include "avformat.h" + +#include "framehook.h" +#include "cmdutils.h" +#include "swscale.h" + +static int sws_flags = SWS_BICUBIC; + +typedef struct { + char filename[2000]; + int x_size; + int y_size; + + /* optimization vars */ + int is_single_frame; + int is_first_frame; + int do_optimize; + int x1; + int x2; + int y1; + int y2; + + /* start/stop/fade vars */ + int start_frame; + int stop_frame; + int fadein_frame; + int fadeout_frame; + int fade_frames; + + /* PiP coord vars */ + int pip_x; + int pip_y; + int pip_w; + int pip_h; + int pip_start_x; + int pip_start_y; + int pip_end_x; + int pip_end_y; + + /* pip_get_frame() variables */ + AVFormatContext *pFormatCtx; + const char *p_ext; + int videoStream; + int frameFinished; + AVCodecContext *pCodecCtx; + AVCodec *pCodec; + AVFrame *pFrame; + AVPacket packet; + int numBytes; + int numFrames; + int masterFrames; + uint8_t *buffer; + int i; + AVInputFormat *file_iformat; + AVStream *st; + int resampled; + int is_done; + AVFrame *pFrameRGB; + int thrR; + int thrG; + int thrB; + int thr_none; + int mode; + float thr_contrast; + + // This vhook first converts frame to RGB ... + struct SwsContext *toRGB_convert_ctx; + // ... then converts a watermark and applies it to the RGB frame ... + struct SwsContext *watermark_convert_ctx; + // ... and finally converts back frame from RGB to initial format + struct SwsContext *fromRGB_convert_ctx; +} ContextInfo; + +int pip_init(ContextInfo *ci); +int pip_get_frame(ContextInfo *ci, int source_width, int source_height); +int pip_cleanup(ContextInfo *ci); + + +/**************************************************************************** + * + ****************************************************************************/ +void Release(void *ctx) +{ + ContextInfo *ci; + ci = (ContextInfo *) ctx; + + if (ci) { + pip_cleanup(ci); + sws_freeContext(ci->toRGB_convert_ctx); + sws_freeContext(ci->watermark_convert_ctx); + sws_freeContext(ci->fromRGB_convert_ctx); + } + av_free(ctx); +} + + +/**************************************************************************** + * + ****************************************************************************/ +int Configure(void **ctxp, int argc, char *argv[]) +{ + ContextInfo *ci; + int c; + int tmp = 0; + + if (0 == (*ctxp = av_mallocz(sizeof(ContextInfo)))) return -1; + ci = (ContextInfo *) *ctxp; + + optind = 1; + + // Initiate the default values. + ci->thrR = ci->thrG = ci->thrB = 0x80; + ci->thr_contrast = 0; + ci->masterFrames = ci->numFrames = 0; + ci->x1 = ci->y1 = INT_MAX; + ci->x2 = ci->y2 = -1; + ci->is_single_frame = ci->is_first_frame = 1; + ci->start_frame = ci->stop_frame = -1; + ci->fadein_frame = ci->fadeout_frame = -1; + ci->fade_frames = 50; + ci->pip_x = ci->pip_y = ci->pip_w = ci->pip_h = -1; + + while ((c = getopt(argc, argv, "f:c:d:m:t:s:e:x:y:w:h:")) > 0) { + switch (c) { + case 'f': + strncpy(ci->filename, optarg, 1999); + ci->filename[1999] = 0; + break; + case 'm': + ci->mode = atoi(optarg); + if (ci->mode == 2) + ci->thr_none = 1; + break; + case 't': + if (1 != sscanf(optarg, "%x", &tmp)) { + av_log(NULL, AV_LOG_ERROR, "PiP: Argument to -t must be a 6 digit hex number\n"); + return -1; + } + ci->thrR = (tmp >> 16) & 0xff; + ci->thrG = (tmp >> 8) & 0xff; + ci->thrB = (tmp >> 0) & 0xff; + break; + case 'c': + ci->thr_contrast = (-atof(optarg)) / 100; + if (ci->thr_contrast < -1 || ci->thr_contrast > 1) { + av_log(NULL, AV_LOG_ERROR, "PiP: Argument to -c must be number between -100 and 100 (percent)\n"); + return -1; + } + break; + case 'd': + ci->fade_frames = atoi(optarg); + break; + case 's': + ci->start_frame = atoi(optarg); + break; + case 'e': + ci->stop_frame = atoi(optarg); + break; + case 'x': + ci->pip_x = atoi(optarg); + if (ci->pip_x < 0) { + av_log(NULL, AV_LOG_ERROR, "PiP: Argument to -x must be positive number\n"); + return -1; + } + break; + case 'y': + ci->pip_y = atoi(optarg); + if (ci->pip_y < 0) { + av_log(NULL, AV_LOG_ERROR, "PiP: Argument to -y must be positive number\n"); + return -1; + } + break; + case 'w': + ci->pip_w = atoi(optarg); + if (ci->pip_w < 0) { + av_log(NULL, AV_LOG_ERROR, "PiP: Argument to -w must be positive number\n"); + return -1; + } + break; + case 'h': + ci->pip_h = atoi(optarg); + if (ci->pip_h < 0) { + av_log(NULL, AV_LOG_ERROR, "PiP: Argument to -h must be positive number\n"); + return -1; + } + break; + default: + av_log(NULL, AV_LOG_ERROR, "PiP: Unrecognized argument '%s'\n", argv[optind]); + return -1; + } + } + + if (ci->filename[0] == 0) { + av_log(NULL, AV_LOG_ERROR, "PiP: There is no file specified.\n"); + return -1; + } + + if (ci->start_frame >= 0) + ci->fadein_frame = ci->start_frame + ci->fade_frames; + if (ci->stop_frame >= ci->fade_frames) + ci->fadeout_frame = ci->stop_frame - ci->fade_frames; + + av_register_all(); + return pip_init(ci); +} + + +/**************************************************************************** + * For mode 0 (the default) + ****************************************************************************/ +static void Process0(void *ctx, + AVPicture *picture, + enum PixelFormat pix_fmt, + int src_width, + int src_height, + int64_t pts) +{ + ContextInfo *ci = (ContextInfo *) ctx; + char *buf = 0; + AVPicture picture1; + AVPicture *pict = picture; + + AVFrame *pFrameRGB; + int xm_size; + int ym_size; + + int x; + int y; + int start_x = 0; + int start_y = 0; + int end_x = src_width; + int end_y = src_height; + int offs, moffs; + uint32_t *p_pixel = 0; + uint32_t pixel_meck; + uint32_t pixel; + uint32_t pixelm; + int tmp; + float thr_strength = 1; + + // Count the frames of the movie source + ci->masterFrames++; + + // Start/Fade In + if (ci->start_frame >= 0) { + if (ci->masterFrames < ci->start_frame) { + // The start frame is set but the current frame is before it + return; + } else { + if (ci->masterFrames < ci->fadein_frame) { + // Ok, we have to fade in. Let's calculate the strength + thr_strength = (float)(ci->fade_frames - (ci->fadein_frame - ci->masterFrames)) / ci->fade_frames; + } + } + } + + // Stop/Fade Out + if (ci->stop_frame >= 0) { + if (ci->masterFrames > ci->stop_frame) { + // The stop frame is set but the current frame is after it + return; + } else { + if (ci->masterFrames > ci->fadeout_frame) { + // Ok, we have to fade out. Let's calculate the strength + thr_strength = (float)(ci->fade_frames - (ci->masterFrames - ci->fadeout_frame)) / ci->fade_frames; + } + } + } + + + // Main source frame convert to RGB format + if (pix_fmt != PIX_FMT_RGBA32) { + int size; + + size = avpicture_get_size(PIX_FMT_RGBA32, src_width, src_height); + buf = av_malloc(size); + + avpicture_fill(&picture1, buf, PIX_FMT_RGBA32, src_width, src_height); + + // if we already got a SWS context, let's realloc if is not re-useable + ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, + src_width, src_height, pix_fmt, + src_width, src_height, PIX_FMT_RGBA32, + sws_flags, NULL, NULL, NULL); + if (ci->toRGB_convert_ctx == NULL) { + av_log(NULL, AV_LOG_ERROR, + "Cannot initialize the toRGB conversion context\n"); + return; + } + sws_scale(ci->toRGB_convert_ctx, + picture->data, picture->linesize, 0, src_height, + picture1.data, picture1.linesize); + + pict = &picture1; + } + + + // Get the next frame of the PiP source + if (0 > pip_get_frame(ci, src_width, src_height)) { + return; + } + + // Setup the working coordinates + start_x = ci->pip_start_x; + start_y = ci->pip_start_y; + end_x = ci->pip_end_x; + end_y = ci->pip_end_y; + + // If optimization flag is up - change the working coordinates + if (ci->do_optimize) { + start_x = ci->x1; + start_y = ci->y1; + end_x = ci->x2; + end_y = ci->y2; + } + + // These are the three original static variables in the ffmpeg hack. + pFrameRGB = ci->pFrameRGB; + xm_size = ci->x_size; + ym_size = ci->y_size; + + // I'll do the *4 => <<2 crap later. Most compilers understand that anyway. + // According to avcodec.h PIX_FMT_RGBA32 is handled in endian specific manner. + for (y=start_y; ypip_y) * (ci->pip_w * 4) + ((start_x - ci->pip_x) * 4); + for (x=start_x; xdata[0])[moffs]); + pixelm = *p_pixel; + + // Check if the PiP colors for this pixel are different than the threshold + if (((pixelm >> 16) & 0xff) != ci->thrR || + ((pixelm >> 8) & 0xff) != ci->thrG || + ((pixelm >> 0) & 0xff) != ci->thrB) + { + // No matter if we optimize or not we can always explore the first frame + if (ci->is_first_frame == 1) { + if (ci->x1 > x) ci->x1 = x; + if (ci->y1 > y) ci->y1 = y; + if (ci->x2 < x) ci->x2 = x; + if (ci->y2 < y) ci->y2 = y; + } + + // Get the pixel from the main source + p_pixel = (uint32_t *)&((pict->data[0])[offs]); + pixel = *p_pixel; + pixel_meck = pixel & 0xff000000; + + // Red + tmp = (int)((pixel >> 16) & 0xff) + (int)(((int)((pixelm >> 16) & 0xff) - ci->thrR - (int)(((int)((pixel >> 16) & 0xff) - ci->thrR) * ci->thr_contrast)) * thr_strength); + if (tmp & (~255)) tmp = ((-tmp) >> 31) & 0xFF; + pixel_meck |= (tmp << 16); + + // Green + tmp = (int)((pixel >> 8 ) & 0xff) + (int)(((int)((pixelm >> 8 ) & 0xff) - ci->thrG - (int)(((int)((pixel >> 8 ) & 0xff) - ci->thrG) * ci->thr_contrast)) * thr_strength); + if (tmp & (~255)) tmp = ((-tmp) >> 31) & 0xFF; + pixel_meck |= (tmp << 8); + + // Blue + tmp = (int)((pixel >> 0 ) & 0xff) + (int)(((int)((pixelm >> 0 ) & 0xff) - ci->thrB - (int)(((int)((pixel >> 0 ) & 0xff) - ci->thrB) * ci->thr_contrast)) * thr_strength); + if (tmp & (~255)) tmp = ((-tmp) >> 31) & 0xFF; + pixel_meck |= (tmp << 0); + + // Apply over the main source pixel + *p_pixel = pixel_meck; + } + offs += 4; + moffs += 4; + } // foreach X + } // foreach Y + + // Optimizations + if (ci->is_single_frame == 1 && !ci->do_optimize && ci->is_first_frame == 0) { + // We have single frame but we still don't optimize + // Set optimization flag if we have all coordinates. Note x2 and y2 are initially -1 + if (ci->x1 >= 0 && ci->y1 >= 0 && ci->x2 >= 0 && ci->y2 >= 0) { + ci->x2++; + ci->y2++; + } else { + // We have a zero pixel image or all pixels are in threshold color + ci->x1 = ci->x2 = ci->y1 = ci->y2 = 0; + } + ci->do_optimize = 1; + av_log(NULL, AV_LOG_ERROR, "\nPiP: Using PiP optimization\nPiP: Detected working area: %d-%d -> %d-%d (size %dx%d)\n", ci->x1, ci->y1, ci->x2, ci->y2, ci->x2 - ci->x1, ci->y2 - ci->y1); + } + ci->is_first_frame = 0; + + // Main source frame convert back to original format + if (pix_fmt != PIX_FMT_RGBA32) { + ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, + src_width, src_height, PIX_FMT_RGBA32, + src_width, src_height, pix_fmt, + sws_flags, NULL, NULL, NULL); + if (ci->fromRGB_convert_ctx == NULL) { + av_log(NULL, AV_LOG_ERROR, + "Cannot initialize the fromRGB conversion context\n"); + return; + } + sws_scale(ci->fromRGB_convert_ctx, + picture1.data, picture1.linesize, 0, src_height, + picture->data, picture->linesize); + } + + av_free(buf); +} + + +/**************************************************************************** + * For mode 1 (the original one) + ****************************************************************************/ +static void Process1(void *ctx, + AVPicture *picture, + enum PixelFormat pix_fmt, + int src_width, + int src_height, + int64_t pts) +{ + ContextInfo *ci = (ContextInfo *) ctx; + char *buf = 0; + AVPicture picture1; + AVPicture *pict = picture; + + AVFrame *pFrameRGB; + int xm_size; + int ym_size; + + int x; + int y; + int start_x = 0; + int start_y = 0; + int end_x = src_width; + int end_y = src_height; + int offs, moffs; + uint32_t *p_pixel = 0; + uint32_t pixel_meck; + uint32_t pixel; + uint32_t pixelm; + int tmp; + float thr_strength = 1; + + // Count the frames of the movie source + ci->masterFrames++; + + + // Start/Fade In + if (ci->start_frame >= 0) { + if (ci->masterFrames < ci->start_frame) { + // The start frame is set but the current frame is before it + return; + } else { + if (ci->masterFrames < ci->fadein_frame) { + // Ok, we have to fade in. Let's calculate the strength + thr_strength = (float)(ci->fade_frames - (ci->fadein_frame - ci->masterFrames)) / ci->fade_frames; + } + } + } + + // Stop/Fade Out + if (ci->stop_frame >= 0) { + if (ci->masterFrames > ci->stop_frame) { + // The stop frame is set but the current frame is after it + return; + } else { + if (ci->masterFrames > ci->fadeout_frame) { + // Ok, we have to fade out. Let's calculate the strength + thr_strength = (float)(ci->fade_frames - (ci->masterFrames - ci->fadeout_frame)) / ci->fade_frames; + } + } + } + + + // Main source frame convert to RGB format + if (pix_fmt != PIX_FMT_RGBA32) { + int size; + + size = avpicture_get_size(PIX_FMT_RGBA32, src_width, src_height); + buf = av_malloc(size); + + avpicture_fill(&picture1, buf, PIX_FMT_RGBA32, src_width, src_height); + + // if we already got a SWS context, let's realloc if is not re-useable + ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, + src_width, src_height, pix_fmt, + src_width, src_height, PIX_FMT_RGBA32, + sws_flags, NULL, NULL, NULL); + if (ci->toRGB_convert_ctx == NULL) { + av_log(NULL, AV_LOG_ERROR, + "Cannot initialize the toRGB conversion context\n"); + return; + } + sws_scale(ci->toRGB_convert_ctx, + picture->data, picture->linesize, 0, src_height, + picture1.data, picture1.linesize); + + pict = &picture1; + } + + + // Get the next frame of the PiP source + if (0 > pip_get_frame(ci, src_width, src_height)) { + return; + } + + start_x = ci->pip_start_x; + start_y = ci->pip_start_y; + end_x = ci->pip_end_x; + end_y = ci->pip_end_y; + + // If optimization flag is up - change the working coordinates + if (ci->do_optimize) { + start_x = ci->x1; + start_y = ci->y1; + end_x = ci->x2; + end_y = ci->y2; + } + + // These are the three original static variables in the ffmpeg hack. + pFrameRGB = ci->pFrameRGB; + xm_size = ci->x_size; + ym_size = ci->y_size; + + // I'll do the *4 => <<2 crap later. Most compilers understand that anyway. + // According to avcodec.h PIX_FMT_RGBA32 is handled in endian specific manner. + for (y=start_y; ypip_y) * (ci->pip_w * 4) + ((start_x - ci->pip_x) * 4); + for (x=start_x; xdata[0])[moffs]); + pixelm = *p_pixel; + + if (((pixelm >> 16) & 0xff) > ci->thrR || + ((pixelm >> 8) & 0xff) > ci->thrG || + ((pixelm >> 0) & 0xff) > ci->thrB || + ci->thr_none) + { + // No matter if we optimize or not we can always explore the first frame + if (ci->is_first_frame == 1) { + if (ci->x1 > x) ci->x1 = x; + if (ci->y1 > y) ci->y1 = y; + if (ci->x2 < x) ci->x2 = x; + if (ci->y2 < y) ci->y2 = y; + } + p_pixel = (uint32_t *)&((pict->data[0])[offs]); + + if (thr_strength != 1) { + // This is needed for the fade in/ fade out process + pixel = *p_pixel; + pixel_meck = pixel & 0xff000000; + + // Red + tmp = (int)(((pixel >> 16) & 0xff) * (1 - thr_strength)) + (int)(((pixelm >> 16) & 0xff) * thr_strength); + if (tmp & (~255)) tmp = ((-tmp) >> 31) & 0xFF; + pixel_meck |= (tmp << 16); + + // Green + tmp = (int)(((pixel >> 8 ) & 0xff) * (1 - thr_strength)) + (int)(((pixelm >> 8 ) & 0xff) * thr_strength); + if (tmp & (~255)) tmp = ((-tmp) >> 31) & 0xFF; + pixel_meck |= (tmp << 8); + + // Blue + tmp = (int)(((pixel >> 0 ) & 0xff) * (1 - thr_strength)) + (int)(((pixelm >> 0 ) & 0xff) * thr_strength); + if (tmp & (~255)) tmp = ((-tmp) >> 31) & 0xFF; + pixel_meck |= (tmp << 0); + + // Apply over the main source pixel + *p_pixel = pixel_meck; + } else { + *p_pixel = pixelm; + } + } + offs += 4; + moffs += 4; + } // foreach X + } // foreach Y + + // Optimizations + if (ci->is_single_frame == 1 && !ci->do_optimize && ci->is_first_frame == 0) { + // We have single frame but we still don't optimize + // Set optimization flag if we have all coordinates. Note x2 and y2 are initially -1 + if (ci->x1 >= 0 && ci->y1 >= 0 && ci->x2 >= 0 && ci->y2 >= 0) { + ci->x2++; + ci->y2++; + } else { + // We have a zero pixel image or all pixels are in threshold color + ci->x1 = ci->x2 = ci->y1 = ci->y2 = 0; + } + ci->do_optimize = 1; + av_log(NULL, AV_LOG_ERROR, "\nPiP: Using PiP optimization\nPiP: Detected working area: %d-%d -> %d-%d (size %dx%d)\n", ci->x1, ci->y1, ci->x2, ci->y2, ci->x2 - ci->x1, ci->y2 - ci->y1); + } + ci->is_first_frame = 0; + + // Main source frame convert back to original format + if (pix_fmt != PIX_FMT_RGBA32) { + ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, + src_width, src_height, PIX_FMT_RGBA32, + src_width, src_height, pix_fmt, + sws_flags, NULL, NULL, NULL); + if (ci->fromRGB_convert_ctx == NULL) { + av_log(NULL, AV_LOG_ERROR, + "Cannot initialize the fromRGB conversion context\n"); + return; + } + sws_scale(ci->fromRGB_convert_ctx, + picture1.data, picture1.linesize, 0, src_height, + picture->data, picture->linesize); + } + + av_free(buf); +} + + +/**************************************************************************** + * This is the function ffmpeg.c callbacks. + ****************************************************************************/ +void Process(void *ctx, + AVPicture *picture, + enum PixelFormat pix_fmt, + int src_width, + int src_height, + int64_t pts) +{ + ContextInfo *ci = (ContextInfo *) ctx; + if (ci->mode >= 1) { + return Process1(ctx, picture, pix_fmt, src_width, src_height, pts); + } else { + return Process0(ctx, picture, pix_fmt, src_width, src_height, pts); + } +} + + +/**************************************************************************** + * Initialize the PiP file. Check formats and etc + ****************************************************************************/ +int pip_init(ContextInfo *ci) +{ + // Yes, *pFrameRGB arguments must be null the first time otherwise it's not good.. + // This block is only executed the first time we enter this function. + if (ci->pFrameRGB == 0) + { + /* + * 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. (Didn't work!) + */ + if (av_open_input_file(&ci->pFormatCtx, ci->filename, NULL, 0, NULL) != 0) { + + // Martin says this should not be necessary but it failed for me sending in + // NULL instead of file_iformat to av_open_input_file() + ci->i = strlen(ci->filename); + if (0 == ci->i) { + av_log(NULL, AV_LOG_ERROR, "pip_init() No filename to watermark vhook\n"); + return -1; + } + while (ci->i > 0) { + if (ci->filename[ci->i] == '.') { + ci->i++; + break; + } + ci->i--; + } + ci->p_ext = &(ci->filename[ci->i]); + ci->file_iformat = av_find_input_format (ci->p_ext); + if (0 == ci->file_iformat) { + av_log(NULL, AV_LOG_INFO, "pip_init() attempt to use image2 for [%s]\n", ci->p_ext); + ci->file_iformat = av_find_input_format ("image2"); + } + if (0 == ci->file_iformat) { + av_log(NULL, AV_LOG_ERROR, "pip_init() Really failed to find iformat [%s]\n", ci->p_ext); + return -1; + } + // now continues the Martin template. + + if (av_open_input_file(&ci->pFormatCtx, ci->filename, ci->file_iformat, 0, NULL)!=0) { + av_log(NULL, AV_LOG_ERROR, "pip_init() Failed to open input file [%s]\n", ci->filename); + return -1; + } + } + + /* + * This fills the streams field of the AVFormatContext with valid information. + */ + if(av_find_stream_info(ci->pFormatCtx)<0) { + av_log(NULL, AV_LOG_ERROR, "pip_init() Failed to find stream info\n"); + return -1; + } + + /* + * As mentioned in the introduction, we'll handle only video streams, not audio + * streams. To make things nice and easy, we simply use the first video stream we + * find. + */ + ci->videoStream=-1; + for(ci->i = 0; ci->i < ci->pFormatCtx->nb_streams; ci->i++) + if(ci->pFormatCtx->streams[ci->i]->codec->codec_type==CODEC_TYPE_VIDEO) + { + ci->videoStream = ci->i; + break; + } + if(ci->videoStream == -1) { + av_log(NULL, AV_LOG_ERROR, "pip_init() Failed to find any video stream\n"); + return -1; + } + + ci->st = ci->pFormatCtx->streams[ci->videoStream]; + + // Get a pointer to the codec context for the video stream + ci->pCodecCtx = ci->pFormatCtx->streams[ci->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 + ci->pCodec = avcodec_find_decoder(ci->pCodecCtx->codec_id); + if(ci->pCodec == NULL) { + av_log(NULL, AV_LOG_ERROR, "pip_init() Failed to find any codec\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 (ci->pCodec->capabilities & CODEC_CAP_TRUNCATED) + ci->pCodecCtx->flags|=CODEC_FLAG_TRUNCATED; + + // Open codec + if(avcodec_open(ci->pCodecCtx, ci->pCodec)<0) { + av_log(NULL, AV_LOG_ERROR, "pip_init() Failed to open codec\n"); + return -1; + } + + // Hack to correct wrong frame rates that seem to be generated by some + // codecs + if (ci->pCodecCtx->time_base.den>1000 && ci->pCodecCtx->time_base.num==1) + ci->pCodecCtx->time_base.num=1000; + + /* + * This is the point where we know if the PiP source is good + */ + } + return 0; +} + + +/**************************************************************************** + * Get next frame + * + * This code follows the example on + * http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html + * + * 0 = ok, -1 = error + ****************************************************************************/ +int pip_get_frame(ContextInfo *ci, int source_width, int source_height) +{ + if (ci->is_done == 1) return 0; + + // The first time *pFrameRGB arguments must be null otherwise it's not good.. + // This block is only executed the first time we enter this function. + if (ci->pFrameRGB == 0) + { + if (!source_width || !source_height) return -1; + + /* + * PiP positioning + */ + + // Defaults + if (ci->pip_x < 0) ci->pip_x = 0; + if (ci->pip_y < 0) ci->pip_y = 0; + if (ci->pip_w < 0) ci->pip_w = source_width; + if (ci->pip_h < 0) ci->pip_h = source_height; + + // Check if coordinates are invalid + if (ci->pip_x >= source_width) ci->pip_x = 0; + if (ci->pip_y >= source_height) ci->pip_y = 0; + + // Check if the dimensions are invalid + if (ci->pip_x + ci->pip_w > source_width) + ci->pip_w = source_width - ci->pip_x; + if (ci->pip_y + ci->pip_h > source_height) + ci->pip_h = source_height - ci->pip_y; + + // Those are used within "for (x)" & "for (y)" + ci->x_size = ci->pip_w; + ci->y_size = ci->pip_h; + ci->pip_start_x = ci->pip_x; + ci->pip_start_y = ci->pip_y; + ci->pip_end_x = ci->pip_x + ci->pip_w; + ci->pip_end_y = ci->pip_y + ci->pip_h; + + if (ci->pip_x > 0 && ci->pip_y) + av_log(NULL, AV_LOG_ERROR, "\nPiP: Position %dx%d, Size %dx%d, Mode %d\n", ci->pip_x, ci->pip_y, ci->pip_w, ci->pip_h, ci->mode); + else + av_log(NULL, AV_LOG_ERROR, "\nPiP: Fullscreen, Size %dx%d, Mode %d\n", ci->pip_w, ci->pip_h, ci->mode); + + if (ci->start_frame >= 0 && ci->stop_frame >= 0) + av_log(NULL, AV_LOG_ERROR, "PiP: Start frame %d, Stop frame %d, Fade in/out: %s\n", ci->start_frame, ci->stop_frame, ci->fade_frames > 0 ? "Yes" : "No"); + + /* + * Allocate a video frame to store the decoded images in. + */ + ci->pFrame = avcodec_alloc_frame(); + + /* + * The RGB image pFrameRGB (of type AVFrame *) is allocated like this: + */ + // Allocate an AVFrame structure + ci->pFrameRGB=avcodec_alloc_frame(); + if(ci->pFrameRGB==NULL) { + av_log(NULL, AV_LOG_ERROR, "pip_get_frame() Failed to alloc pFrameRGB\n"); + return -1; + } + + // Determine required buffer size and allocate buffer + ci->numBytes = avpicture_get_size(PIX_FMT_RGBA32, ci->x_size, ci->y_size); + ci->buffer = av_malloc(ci->numBytes); + + // Assign appropriate parts of buffer to image planes in pFrameRGB + avpicture_fill((AVPicture *)ci->pFrameRGB, ci->buffer, PIX_FMT_RGBA32, + ci->x_size, ci->y_size); + } + + // Get frame + while(av_read_frame(ci->pFormatCtx, &ci->packet)>=0) + { + // Is this a packet from the video stream? + if(ci->packet.stream_index == ci->videoStream) + { + // Count the PiP source frames + ci->numFrames++; + if (ci->numFrames > 1) + ci->is_single_frame = 0; + + // Decode video frame + avcodec_decode_video(ci->pCodecCtx, ci->pFrame, &ci->frameFinished, + ci->packet.data, ci->packet.size); + + // Did we get a video frame? + if(ci->frameFinished) + { + // Convert the image from its native format to RGBA32 + ci->watermark_convert_ctx = + sws_getCachedContext(ci->watermark_convert_ctx, + ci->pCodecCtx->width, ci->pCodecCtx->height, ci->pCodecCtx->pix_fmt, + ci->x_size, ci->y_size, PIX_FMT_RGBA32, + sws_flags, NULL, NULL, NULL); + if (ci->watermark_convert_ctx == NULL) { + av_log(NULL, AV_LOG_ERROR, + "pip_get_frame() Cannot initialize the watermark conversion context\n"); + return -1; + } + sws_scale(ci->watermark_convert_ctx, + ci->pFrame->data, ci->pFrame->linesize, 0, ci->y_size, + ci->pFrameRGB->data, ci->pFrameRGB->linesize); + + // We got it. Free the packet since we are returning + av_free_packet(&ci->packet); + return 0; + } + } + + // Free the packet that was allocated by av_read_frame + av_free_packet(&ci->packet); + } + // On multi-frame source we should stop the mixing process when + // the PiP source don't have more frames + if (ci->numFrames > 1) + ci->stop_frame = 0; + ci->is_done = 1; + return 0; +} + +int pip_cleanup(ContextInfo *ci) +{ + // Free the RGB image + av_freep(&ci->buffer); + av_freep(&ci->pFrameRGB); + + // Close the codec + if (0 != ci->pCodecCtx) { + avcodec_close(ci->pCodecCtx); + ci->pCodecCtx = 0; + } + + // Close the video file + if (0 != ci->pFormatCtx) { + av_close_input_file(ci->pFormatCtx); + ci->pFormatCtx = 0; + } + + ci->is_done = 0; + return 0; +} + + +void parse_arg_file(const char *filename) +{ +}