Added three sample video hooks. See the (rudimentary) documentation on what

they do and their capabilities.

Originally committed as revision 1265 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Philip Gladstone 2002-11-21 03:50:36 +00:00
parent 65e70450de
commit 26b4bb70c2
6 changed files with 841 additions and 0 deletions

27
vhook/Makefile Normal file
View File

@ -0,0 +1,27 @@
include ../config.mak
CFLAGS=-fPIC $(OPTFLAGS) -g -Wall -I.. -I../libav -I../libavcodec -DHAVE_AV_CONFIG_H
HOOKS=null.so fish.so
ifeq ($(HAVE_IMLIB2),yes)
HOOKS += imlib2.so
endif
all: $(HOOKS) hooks.html
install:
install -s -m 755 $(HOOKS) $(INSTDIR)
imlib2.so: imlib2.o
$(CC) -g -o $@ -shared $< -lImlib2
rm $<
%.so: %.o
$(CC) -g -o $@ -shared $<
%.html: %.texi
texi2html -monolithic -number $<
clean:
rm -f *.o *.so *~

330
vhook/fish.c Normal file
View File

@ -0,0 +1,330 @@
/*
* Fish Detector Hook
* Copyright (c) 2002 Philip Gladstone
*
* This file implements a fish detector. It is used to see when a
* goldfish passes in front of the camera. It does this by counting
* the number of input pixels that fall within a particular HSV
* range.
*
* It takes a multitude of arguments:
*
* -h <num>-<num> the range of H values that are fish
* -s <num>-<num> the range of S values that are fish
* -v <num>-<num> the range of V values that are fish
* -z zap all non-fish values to black
* -l <num> limit the number of saved files to <num>
* -i <num> only check frames every <num> seconds
* -t <num> the threshold for the amount of fish pixels (range 0-1)
* -d turn debugging on
* -D <directory> where to put the fish images
*
* This library 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 of the License, or (at your option) any later version.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <stdio.h>
#include <dirent.h>
#include "framehook.h"
#include "dsputil.h"
#define SCALE_BITS 10
#define C_Y (76309 >> (16 - SCALE_BITS))
#define C_RV (117504 >> (16 - SCALE_BITS))
#define C_BU (138453 >> (16 - SCALE_BITS))
#define C_GU (13954 >> (16 - SCALE_BITS))
#define C_GV (34903 >> (16 - SCALE_BITS))
typedef struct {
int h; /* 0 .. 360 */
int s; /* 0 .. 255 */
int v; /* 0 .. 255 */
} HSV;
typedef struct {
int zapping;
int threshold;
HSV dark, bright;
char *dir;
int file_limit;
int debug;
int min_interval;
INT64 next_pts;
int inset;
int min_width;
} ContextInfo;
static void dorange(const char *s, int *first, int *second, int maxval)
{
sscanf(s, "%d-%d", first, second);
if (*first > maxval)
*first = maxval;
if (*second > maxval)
*second = maxval;
}
int Configure(void **ctxp, int argc, char *argv[])
{
ContextInfo *ci;
int c;
*ctxp = av_mallocz(sizeof(ContextInfo));
ci = (ContextInfo *) *ctxp;
optind = 0;
ci->dir = "/tmp";
ci->threshold = 1000;
ci->file_limit = 100;
ci->min_interval = 1000000;
ci->inset = 10; /* Percent */
while ((c = getopt(argc, argv, "w:i:dh:s:v:zl:t:D:")) > 0) {
switch (c) {
case 'h':
dorange(optarg, &ci->dark.h, &ci->bright.h, 360);
break;
case 's':
dorange(optarg, &ci->dark.s, &ci->bright.s, 255);
break;
case 'v':
dorange(optarg, &ci->dark.v, &ci->bright.v, 255);
break;
case 'z':
ci->zapping = 1;
break;
case 'l':
ci->file_limit = atoi(optarg);
break;
case 'i':
ci->min_interval = 1000000 * atof(optarg);
break;
case 't':
ci->threshold = atof(optarg) * 1000;
break;
case 'w':
ci->min_width = atoi(optarg);
break;
case 'd':
ci->debug++;
break;
case 'D':
ci->dir = strdup(optarg);
break;
default:
fprintf(stderr, "Unrecognized argument '%s'\n", argv[optind]);
return -1;
}
}
fprintf(stderr, "Fish detector configured:\n");
fprintf(stderr, " HSV range: %d,%d,%d - %d,%d,%d\n",
ci->dark.h,
ci->dark.s,
ci->dark.v,
ci->bright.h,
ci->bright.s,
ci->bright.v);
return 0;
}
static void get_hsv(HSV *hsv, int r, int g, int b)
{
int i, v, x, f;
x = (r < g) ? r : g;
if (b < x)
x = b;
v = (r > g) ? r : g;
if (b > v)
v = b;
if (v == x) {
hsv->h = 0;
hsv->s = 0;
hsv->v = v;
return;
}
if (r == v) {
f = g - b;
i = 0;
} else if (g == v) {
f = b - r;
i = 2 * 60;
} else {
f = r - g;
i = 4 * 60;
}
hsv->h = i + (60 * f) / (v - x);
if (hsv->h < 0)
hsv->h += 360;
hsv->s = (255 * (v - x)) / v;
hsv->v = v;
return;
}
void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, INT64 pts)
{
ContextInfo *ci = (ContextInfo *) ctx;
UINT8 *cm = cropTbl + MAX_NEG_CROP;
int rowsize = picture->linesize[0];
if (pts < ci->next_pts)
return;
if (width < ci->min_width)
return;
ci->next_pts = pts + 1000000;
if (pix_fmt == PIX_FMT_YUV420P) {
UINT8 *y, *u, *v;
int width2 = width >> 1;
int inrange = 0;
int pixcnt;
int h;
int h_start, h_end;
int w_start, w_end;
h_end = 2 * ((ci->inset * height) / 200);
h_start = height - h_end;
w_end = (ci->inset * width2) / 100;
w_start = width2 - w_end;
pixcnt = ((h_start - h_end) >> 1) * (w_start - w_end);
y = picture->data[0];
u = picture->data[1];
v = picture->data[2];
for (h = h_start; h > h_end; h -= 2) {
int w;
for (w = w_start; w > w_end; w--) {
int r,g,b;
int Y, U, V;
HSV hsv;
U = u[0] - 128;
V = v[0] - 128;
Y = (y[0] - 16) * C_Y;
r = cm[(Y + C_RV * V + (1 << (SCALE_BITS - 1))) >> SCALE_BITS];
g = cm[(Y + - C_GU * U - C_GV * V + (1 << (SCALE_BITS - 1))) >> SCALE_BITS];
b = cm[(Y + C_BU * U + (1 << (SCALE_BITS - 1))) >> SCALE_BITS];
get_hsv(&hsv, r, g, b);
if (ci->debug > 1)
fprintf(stderr, "(%d,%d,%d) -> (%d,%d,%d)\n",
r,g,b,hsv.h,hsv.s,hsv.v);
if (hsv.h >= ci->dark.h && hsv.h <= ci->bright.h &&
hsv.s >= ci->dark.s && hsv.s <= ci->bright.s &&
hsv.v >= ci->dark.v && hsv.v <= ci->bright.v) {
inrange++;
} else if (ci->zapping) {
y[0] = y[1] = y[rowsize] = y[rowsize + 1] = 0;
}
y+= 2;
u++;
v++;
}
y += picture->linesize[0] * 2 - width;
u += picture->linesize[1] - width2;
v += picture->linesize[2] - width2;
}
if (inrange * 1000 / pixcnt >= ci->threshold) {
/* Save to file */
int size;
char *buf;
AVPicture picture1;
static int frame_counter;
static int foundfile;
if (ci->debug)
fprintf(stderr, "Fish: Inrange=%d of %d = %d threshold\n", inrange, pixcnt, 1000 * inrange / pixcnt);
if ((frame_counter++ % 20) == 0) {
/* Check how many files we have */
DIR *d;
foundfile = 0;
d = opendir(ci->dir);
if (d) {
struct dirent *dent;
while ((dent = readdir(d))) {
if (strncmp("fishimg", dent->d_name, 7) == 0) {
if (strcmp(".ppm", dent->d_name + strlen(dent->d_name) - 4) == 0) {
foundfile++;
}
}
}
closedir(d);
}
}
if (foundfile < ci->file_limit) {
size = avpicture_get_size(PIX_FMT_RGB24, width, height);
buf = av_malloc(size);
avpicture_fill(&picture1, buf, PIX_FMT_RGB24, width, height);
if (img_convert(&picture1, PIX_FMT_RGB24,
picture, pix_fmt, width, height) >= 0) {
/* Write out the PPM file */
FILE *f;
char fname[256];
sprintf(fname, "%s/fishimg%ld_%lld.ppm", ci->dir, time(0), pts);
f = fopen(fname, "w");
if (f) {
fprintf(f, "P6 %d %d 255\n", width, height);
fwrite(buf, width * height * 3, 1, f);
fclose(f);
}
}
av_free(buf);
ci->next_pts = pts + ci->min_interval;
}
}
}
}
/* To ensure correct typing */
FrameHookConfigureFn ConfigureFn = Configure;
FrameHookProcessFn ProcessFn = Process;

95
vhook/hooks.html Normal file
View File

@ -0,0 +1,95 @@
<HTML>
<HEAD>
<!-- This HTML file has been created by texi2html 1.51
from hooks.texi on 20 November 2002 -->
<TITLE>Video Hook Documentation</TITLE>
</HEAD>
<BODY>
<H1>Video Hook Documentation</H1>
<P>
<P><HR><P>
<H1>Table of Contents</H1>
<UL>
<LI><A NAME="TOC1" HREF="hooks.html#SEC1">1 Introduction</A>
<UL>
<LI><A NAME="TOC2" HREF="hooks.html#SEC2">1.1 null.c</A>
<LI><A NAME="TOC3" HREF="hooks.html#SEC3">1.2 fish.c</A>
<LI><A NAME="TOC4" HREF="hooks.html#SEC4">1.3 imlib2.c</A>
</UL>
</UL>
<P><HR><P>
<P>
Video Hook Documentation
<P>
</P>
<H1><A NAME="SEC1" HREF="hooks.html#TOC1">1 Introduction</A></H1>
<P>
The video hook functionality is designed (mostly) for live video. It allows
the video to be modified or examined between the decoder and the encoder.
</P>
<P>
Any number of hook modules can be placed inline, and they are run in the
order that they were specified on the ffmpeg command line.
</P>
<P>
Three modules are provided and are described below. They are all intended to
be used as a base for your own modules.
</P>
<P>
Modules are loaded using the -vhook option to ffmpeg. The value of this parameter
is a space seperated list of arguments. The first is the module name, and the rest
are passed as arguments to the Configure function of the module.
</P>
<H2><A NAME="SEC2" HREF="hooks.html#TOC2">1.1 null.c</A></H2>
<P>
This does nothing. Actually it converts the input image to RGB24 and then converts
it back again. This is meant as a sample that you can use to test your setup.
</P>
<H2><A NAME="SEC3" HREF="hooks.html#TOC3">1.2 fish.c</A></H2>
<P>
This implements a 'fish detector'. Essentially it converts the image into HSV
space and tests whether more than a certain percentage of the pixels fall into
a specific HSV cuboid. If so, then the image is saved into a file for processing
by other bits of code.
</P>
<P>
Why use HSV? It turns out that HSV cuboids represent a more compact range of
colors than would an RGB cuboid.
</P>
<H2><A NAME="SEC4" HREF="hooks.html#TOC4">1.3 imlib2.c</A></H2>
<P>
This allows a caption to be placed onto each frame. It supports inserting the
time and date. By using the imlib functions, it would be easy to add your own
graphical logo, add a frame/border, etc.
</P>
<P><HR><P>
This document was generated on 20 November 2002 using the
<A HREF="http://wwwcn.cern.ch/dci/texi2html/">texi2html</A>
translator version 1.51.</P>
</BODY>
</HTML>

49
vhook/hooks.texi Normal file
View File

@ -0,0 +1,49 @@
\input texinfo @c -*- texinfo -*-
@settitle Video Hook Documentation
@titlepage
@sp 7
@center @titlefont{Video Hook Documentation}
@sp 3
@end titlepage
@chapter Introduction
The video hook functionality is designed (mostly) for live video. It allows
the video to be modified or examined between the decoder and the encoder.
Any number of hook modules can be placed inline, and they are run in the
order that they were specified on the ffmpeg command line.
Three modules are provided and are described below. They are all intended to
be used as a base for your own modules.
Modules are loaded using the -vhook option to ffmpeg. The value of this parameter
is a space seperated list of arguments. The first is the module name, and the rest
are passed as arguments to the Configure function of the module.
@section null.c
This does nothing. Actually it converts the input image to RGB24 and then converts
it back again. This is meant as a sample that you can use to test your setup.
@section fish.c
This implements a 'fish detector'. Essentially it converts the image into HSV
space and tests whether more than a certain percentage of the pixels fall into
a specific HSV cuboid. If so, then the image is saved into a file for processing
by other bits of code.
Why use HSV? It turns out that HSV cuboids represent a more compact range of
colors than would an RGB cuboid.
@section imlib2.c
This allows a caption to be placed onto each frame. It supports inserting the
time and date. By using the imlib functions, it would be easy to add your own
graphical logo, add a frame/border, etc.
@bye

267
vhook/imlib2.c Normal file
View File

@ -0,0 +1,267 @@
/*
* imlib2 based hook
* Copyright (c) 2002 Philip Gladstone
*
* This module implements a text overlay for a video image. Currently it
* supports a fixed overlay or reading the text from a file. The string
* is passed through strftime so that it is easy to imprint the date and
* time onto the image.
*
* Options:
*
* -c <color> The color of the text
* -F <fontname> The font face and size
* -t <text> The text
* -f <filename> The filename to read text from
* -x <num> X coordinate to start text
* -y <num> Y coordinate to start text
*
* This module is very much intended as an example of what could be done.
* For example, you could overlay an image (even semi-transparent) like
* TV stations do. You can manipulate the image using imlib2 functions
* in any way.
*
* One caution is that this is an expensive process -- in particular the
* conversion of the image into RGB and back is time consuming. For some
* special cases -- e.g. painting black text -- it would be faster to paint
* the text into a bitmap and then combine it directly into the YUV
* image. However, this code is fast enough to handle 10 fps of 320x240 on a
* 900MHz Duron in maybe 15% of the CPU.
*
* This library 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 of the License, or (at your option) any later version.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <Imlib2.h>
#include "framehook.h"
typedef struct {
int dummy;
Imlib_Font fn;
char *text;
char *file;
int r, g, b;
int x;
int y;
struct _CachedImage *cache;
} ContextInfo;
typedef struct _CachedImage {
struct _CachedImage *next;
Imlib_Image image;
int width;
int height;
} CachedImage;
int Configure(void **ctxp, int argc, char *argv[])
{
int c;
ContextInfo *ci;
char *font = "LucidaSansDemiBold/16";
char *fp = getenv("FONTPATH");
char *color = 0;
FILE *f;
*ctxp = av_mallocz(sizeof(ContextInfo));
ci = (ContextInfo *) *ctxp;
optind = 0;
if (fp)
imlib_add_path_to_font_path(fp);
while ((c = getopt(argc, argv, "c:f:F:t:x:y:")) > 0) {
switch (c) {
case 'c':
color = optarg;
break;
case 'F':
font = optarg;
break;
case 't':
ci->text = strdup(optarg);
break;
case 'f':
ci->file = strdup(optarg);
break;
case 'x':
ci->x = atoi(optarg);
break;
case 'y':
ci->y = atoi(optarg);
break;
case '?':
fprintf(stderr, "Unrecognized argument '%s'\n", argv[optind]);
return -1;
}
}
ci->fn = imlib_load_font(font);
if (!ci->fn) {
fprintf(stderr, "Failed to load font '%s'\n", font);
return -1;
}
imlib_context_set_font(ci->fn);
imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT);
if (color) {
char buff[256];
int done = 0;
f = fopen("/usr/lib/X11/rgb.txt", "r");
if (!f) {
fprintf(stderr, "Failed to find rgb.txt\n");
return -1;
}
while (fgets(buff, sizeof(buff), f)) {
int r, g, b;
char colname[80];
if (sscanf(buff, "%d %d %d %64s", &r, &g, &b, colname) == 4 &&
strcasecmp(colname, color) == 0) {
ci->r = r;
ci->g = g;
ci->b = b;
/* fprintf(stderr, "%s -> %d,%d,%d\n", colname, r, g, b); */
done = 1;
break;
}
}
fclose(f);
if (!done) {
fprintf(stderr, "Unable to find color '%s' in rgb.txt\n", color);
return -1;
}
}
imlib_context_set_color(ci->r, ci->g, ci->b, 255);
return 0;
}
static Imlib_Image get_cached_image(ContextInfo *ci, int width, int height)
{
CachedImage *cache;
for (cache = ci->cache; cache; cache = cache->next) {
if (width == cache->width && height == cache->height)
return cache->image;
}
return NULL;
}
static void put_cached_image(ContextInfo *ci, Imlib_Image image, int width, int height)
{
CachedImage *cache = av_mallocz(sizeof(*cache));
cache->image = image;
cache->width = width;
cache->height = height;
cache->next = ci->cache;
ci->cache = cache;
}
void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, INT64 pts)
{
ContextInfo *ci = (ContextInfo *) ctx;
AVPicture picture1;
Imlib_Image image;
DATA32 *data;
image = get_cached_image(ci, width, height);
if (!image) {
image = imlib_create_image(width, height);
put_cached_image(ci, image, width, height);
}
imlib_context_set_image(image);
data = imlib_image_get_data();
if (pix_fmt != PIX_FMT_BGRA32) {
avpicture_fill(&picture1, (UINT8 *) data, PIX_FMT_BGRA32, width, height);
if (img_convert(&picture1, PIX_FMT_BGRA32,
picture, pix_fmt, width, height) < 0) {
goto done;
}
} else {
av_abort();
}
imlib_image_set_has_alpha(0);
{
int wid, hig, h_a, v_a;
char buff[1000];
char tbuff[1000];
char *tbp = ci->text;
time_t now = time(0);
char *p, *q;
int x, y;
if (ci->file) {
int fd = open(ci->file, O_RDONLY);
if (fd < 0) {
tbp = "[File not found]";
} else {
int l = read(fd, tbuff, sizeof(tbuff) - 1);
if (l >= 0) {
tbuff[l] = 0;
tbp = tbuff;
} else {
tbp = "[I/O Error]";
}
close(fd);
}
}
strftime(buff, sizeof(buff), tbp, localtime(&now));
x = ci->x;
y = ci->y;
for (p = buff; p; p = q) {
q = strchr(p, '\n');
if (q)
*q++ = 0;
imlib_text_draw_with_return_metrics(x, y, p, &wid, &hig, &h_a, &v_a);
y += v_a;
}
}
if (pix_fmt != PIX_FMT_BGRA32) {
if (img_convert(picture, pix_fmt,
&picture1, PIX_FMT_BGRA32, width, height) < 0) {
}
}
done:
;
}
/* To ensure correct typing */
FrameHookConfigureFn ConfigureFn = Configure;
FrameHookProcessFn ProcessFn = Process;

73
vhook/null.c Normal file
View File

@ -0,0 +1,73 @@
/*
* Null Video Hook
* Copyright (c) 2002 Philip Gladstone
*
* This library 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 of the License, or (at your option) any later version.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include "framehook.h"
typedef struct {
int dummy;
} ContextInfo;
int Configure(void **ctxp, int argc, char *argv[])
{
fprintf(stderr, "Called with argc=%d\n", argc);
*ctxp = av_mallocz(sizeof(ContextInfo));
return 0;
}
void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, INT64 pts)
{
ContextInfo *ci = (ContextInfo *) ctx;
char *buf = 0;
AVPicture picture1;
AVPicture *pict = picture;
(void) ci;
if (pix_fmt != PIX_FMT_RGB24) {
int size;
size = avpicture_get_size(PIX_FMT_RGB24, width, height);
buf = av_malloc(size);
avpicture_fill(&picture1, buf, PIX_FMT_RGB24, width, height);
if (img_convert(&picture1, PIX_FMT_RGB24,
picture, pix_fmt, width, height) < 0) {
av_free(buf);
return;
}
pict = &picture1;
}
/* Insert filter code here */
if (pix_fmt != PIX_FMT_RGB24) {
if (img_convert(picture, pix_fmt,
&picture1, PIX_FMT_RGB24, width, height) < 0) {
}
}
av_free(buf);
}
/* To ensure correct typing */
FrameHookConfigureFn ConfigureFn = Configure;
FrameHookProcessFn ProcessFn = Process;