00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046 #define MAXSIZE_TEXT 1024
00047
00048 #include "libavformat/framehook.h"
00049
00050 #include <stdio.h>
00051 #include <stdlib.h>
00052 #include <fcntl.h>
00053 #include <stdarg.h>
00054 #include <string.h>
00055 #include <unistd.h>
00056 #undef time
00057 #include <sys/time.h>
00058 #include <time.h>
00059
00060 #include <ft2build.h>
00061 #include FT_FREETYPE_H
00062 #include FT_GLYPH_H
00063
00064 #define SCALEBITS 10
00065 #define ONE_HALF (1 << (SCALEBITS - 1))
00066 #define FIX(x) ((int) ((x) * (1<<SCALEBITS) + 0.5))
00067
00068 #define RGB_TO_YUV(rgb_color, yuv_color) do { \
00069 yuv_color[0] = (FIX(0.29900) * rgb_color[0] + FIX(0.58700) * rgb_color[1] + FIX(0.11400) * rgb_color[2] + ONE_HALF) >> SCALEBITS; \
00070 yuv_color[2] = ((FIX(0.50000) * rgb_color[0] - FIX(0.41869) * rgb_color[1] - FIX(0.08131) * rgb_color[2] + ONE_HALF - 1) >> SCALEBITS) + 128; \
00071 yuv_color[1] = ((- FIX(0.16874) * rgb_color[0] - FIX(0.33126) * rgb_color[1] + FIX(0.50000) * rgb_color[2] + ONE_HALF - 1) >> SCALEBITS) + 128; \
00072 } while (0)
00073
00074 #define COPY_3(dst,src) { \
00075 dst[0]=src[0]; \
00076 dst[1]=src[1]; \
00077 dst[2]=src[2]; \
00078 }
00079
00080
00081
00082 #define SET_PIXEL(picture, yuv_color, x, y) { \
00083 picture->data[0][ (x) + (y)*picture->linesize[0] ] = yuv_color[0]; \
00084 picture->data[1][ ((x/2) + (y/2)*picture->linesize[1]) ] = yuv_color[1]; \
00085 picture->data[2][ ((x/2) + (y/2)*picture->linesize[2]) ] = yuv_color[2]; \
00086 }
00087
00088 #define GET_PIXEL(picture, yuv_color, x, y) { \
00089 yuv_color[0] = picture->data[0][ (x) + (y)*picture->linesize[0] ]; \
00090 yuv_color[1] = picture->data[1][ (x/2) + (y/2)*picture->linesize[1] ]; \
00091 yuv_color[2] = picture->data[2][ (x/2) + (y/2)*picture->linesize[2] ]; \
00092 }
00093
00094
00095 typedef struct {
00096 unsigned char *text;
00097 char *file;
00098 unsigned int x;
00099 unsigned int y;
00100 int bg;
00101 int outline;
00102 unsigned char bgcolor[3];
00103 unsigned char fgcolor[3];
00104 FT_Library library;
00105 FT_Face face;
00106 FT_Glyph glyphs[ 255 ];
00107 FT_Bitmap bitmaps[ 255 ];
00108 int advance[ 255 ];
00109 int bitmap_left[ 255 ];
00110 int bitmap_top[ 255 ];
00111 unsigned int glyphs_index[ 255 ];
00112 int text_height;
00113 int baseline;
00114 int use_kerning;
00115 } ContextInfo;
00116
00117
00118 void Release(void *ctx)
00119 {
00120 if (ctx)
00121 av_free(ctx);
00122 }
00123
00124
00125 static int ParseColor(char *text, unsigned char yuv_color[3])
00126 {
00127 char tmp[3];
00128 unsigned char rgb_color[3];
00129 int i;
00130
00131 tmp[2] = '\0';
00132
00133 if ((!text) || (strlen(text) != 7) || (text[0] != '#') )
00134 return -1;
00135
00136 for (i=0; i < 3; i++)
00137 {
00138 tmp[0] = text[i*2+1];
00139 tmp[1] = text[i*2+2];
00140
00141 rgb_color[i] = strtol(tmp, NULL, 16);
00142 }
00143
00144 RGB_TO_YUV(rgb_color, yuv_color);
00145
00146 return 0;
00147 }
00148
00149 int Configure(void **ctxp, int argc, char *argv[])
00150 {
00151 int c;
00152 int error;
00153 ContextInfo *ci=NULL;
00154 char *font=NULL;
00155 unsigned int size=16;
00156 FT_BBox bbox;
00157 int yMax, yMin;
00158 *ctxp = av_mallocz(sizeof(ContextInfo));
00159 ci = (ContextInfo *) *ctxp;
00160
00161
00162 ci->text = NULL;
00163 ci->file = NULL;
00164 ci->x = ci->y = 0;
00165 ci->fgcolor[0]=255;
00166 ci->fgcolor[1]=128;
00167 ci->fgcolor[2]=128;
00168 ci->bgcolor[0]=0;
00169 ci->fgcolor[1]=128;
00170 ci->fgcolor[2]=128;
00171 ci->bg = 0;
00172 ci->outline = 0;
00173 ci->text_height = 0;
00174
00175 optind = 1;
00176 while ((c = getopt(argc, argv, "f:t:T:x:y:s:c:C:bo")) > 0) {
00177 switch (c) {
00178 case 'f':
00179 font = optarg;
00180 break;
00181 case 't':
00182 ci->text = av_strdup(optarg);
00183 break;
00184 case 'T':
00185 ci->file = av_strdup(optarg);
00186 break;
00187 case 'x':
00188 ci->x = (unsigned int) atoi(optarg);
00189 break;
00190 case 'y':
00191 ci->y = (unsigned int) atoi(optarg);
00192 break;
00193 case 's':
00194 size = (unsigned int) atoi(optarg);
00195 break;
00196 case 'c':
00197 if (ParseColor(optarg, ci->fgcolor) == -1)
00198 {
00199 av_log(NULL, AV_LOG_ERROR, "Invalid foreground color: '%s'. You must specify the color in the internet way(packaged hex): #RRGGBB, ie: -c #ffffff (for white foreground)\n", optarg);
00200 return -1;
00201 }
00202 break;
00203 case 'C':
00204 if (ParseColor(optarg, ci->bgcolor) == -1)
00205 {
00206 av_log(NULL, AV_LOG_ERROR, "Invalid background color: '%s'. You must specify the color in the internet way(packaged hex): #RRGGBB, ie: -C #ffffff (for white background)\n", optarg);
00207 return -1;
00208 }
00209 break;
00210 case 'b':
00211 ci->bg=1;
00212 break;
00213 case 'o':
00214 ci->outline=1;
00215 break;
00216 case '?':
00217 av_log(NULL, AV_LOG_ERROR, "Unrecognized argument '%s'\n", argv[optind]);
00218 return -1;
00219 }
00220 }
00221
00222 if (!ci->text)
00223 {
00224 av_log(NULL, AV_LOG_ERROR, "No text provided (-t text)\n");
00225 return -1;
00226 }
00227
00228 if (ci->file)
00229 {
00230 FILE *fp;
00231 if ((fp=fopen(ci->file, "r")) == NULL)
00232 {
00233 av_log(NULL, AV_LOG_INFO, "WARNING: The file could not be opened. Using text provided with -t switch: %s", strerror(errno));
00234 }
00235 else
00236 {
00237 fclose(fp);
00238 }
00239 }
00240
00241 if (!font)
00242 {
00243 av_log(NULL, AV_LOG_ERROR, "No font file provided! (-f filename)\n");
00244 return -1;
00245 }
00246
00247 if ((error = FT_Init_FreeType(&(ci->library))) != 0)
00248 {
00249 av_log(NULL, AV_LOG_ERROR, "Could not load FreeType (error# %d).\n", error);
00250 return -1;
00251 }
00252
00253 if ((error = FT_New_Face( ci->library, font, 0, &(ci->face) )) != 0)
00254 {
00255 av_log(NULL, AV_LOG_ERROR, "Could not load face: %s (error# %d).\n", font, error);
00256 return -1;
00257 }
00258
00259 if ((error = FT_Set_Pixel_Sizes( ci->face, 0, size)) != 0)
00260 {
00261 av_log(NULL, AV_LOG_ERROR, "Could not set font size to %d pixels (error# %d).\n", size, error);
00262 return -1;
00263 }
00264
00265 ci->use_kerning = FT_HAS_KERNING(ci->face);
00266
00267
00268 yMax = -32000;
00269 yMin = 32000;
00270 for (c=0; c < 256; c++)
00271 {
00272
00273 error = FT_Load_Char( ci->face, (unsigned char) c, FT_LOAD_RENDER | FT_LOAD_MONOCHROME );
00274 if (error) continue;
00275
00276
00277 ci->bitmaps[c] = ci->face->glyph->bitmap;
00278
00279 ci->bitmap_left[c] = ci->face->glyph->bitmap_left;
00280
00281 ci->bitmap_top[c] = ci->face->glyph->bitmap_top;
00282
00283
00284 ci->advance[c] = ci->face->glyph->advance.x >> 6;
00285
00286
00287 error = FT_Get_Glyph( ci->face->glyph, &(ci->glyphs[c]) );
00288
00289 ci->glyphs_index[c] = FT_Get_Char_Index( ci->face, (unsigned char) c );
00290
00291
00292 FT_Glyph_Get_CBox( ci->glyphs[ c ], ft_glyph_bbox_pixels, &bbox );
00293 if (bbox.yMax > yMax)
00294 yMax = bbox.yMax;
00295 if (bbox.yMin < yMin)
00296 yMin = bbox.yMin;
00297
00298 }
00299
00300 ci->text_height = yMax - yMin;
00301 ci->baseline = yMax;
00302
00303 return 0;
00304 }
00305
00306
00307
00308
00309 static inline void draw_glyph(AVPicture *picture, FT_Bitmap *bitmap, unsigned int x, unsigned int y, unsigned int width, unsigned int height, unsigned char yuv_fgcolor[3], unsigned char yuv_bgcolor[3], int outline)
00310 {
00311 int r, c;
00312 int spixel, dpixel[3], in_glyph=0;
00313
00314 if (bitmap->pixel_mode == ft_pixel_mode_mono)
00315 {
00316 in_glyph = 0;
00317 for (r=0; (r < bitmap->rows) && (r+y < height); r++)
00318 {
00319 for (c=0; (c < bitmap->width) && (c+x < width); c++)
00320 {
00321
00322 GET_PIXEL(picture, dpixel, (c+x), (y+r));
00323
00324
00325 spixel = bitmap->buffer[r*bitmap->pitch +c/8] & (0x80>>(c%8));
00326
00327 if (spixel)
00328 COPY_3(dpixel, yuv_fgcolor);
00329
00330 if (outline)
00331 {
00332
00333 if ( (!in_glyph) && (spixel) )
00334
00335 {
00336 in_glyph = 1;
00337
00338 if (c-1 >= 0)
00339 SET_PIXEL(picture, yuv_bgcolor, (c+x-1), (y+r));
00340 }
00341 else if ( (in_glyph) && (!spixel) )
00342
00343 {
00344 in_glyph = 0;
00345
00346 COPY_3(dpixel, yuv_bgcolor);
00347 }
00348
00349 if (in_glyph)
00350
00351 {
00352
00353 if ( (r-1 >= 0) && (! bitmap->buffer[(r-1)*bitmap->pitch +c/8] & (0x80>>(c%8))) )
00354
00355 SET_PIXEL(picture, yuv_bgcolor, (c+x), (y+r-1));
00356
00357
00358 if ( (r+1 < height) && (! bitmap->buffer[(r+1)*bitmap->pitch +c/8] & (0x80>>(c%8))) )
00359
00360 SET_PIXEL(picture, yuv_bgcolor, (c+x), (y+r+1));
00361
00362 }
00363 }
00364
00365 SET_PIXEL(picture, dpixel, (c+x), (y+r));
00366 }
00367 }
00368 }
00369 }
00370
00371
00372 static inline void draw_box(AVPicture *picture, unsigned int x, unsigned int y, unsigned int width, unsigned int height, unsigned char yuv_color[3])
00373 {
00374 int i, j;
00375
00376 for (j = 0; (j < height); j++)
00377 for (i = 0; (i < width); i++)
00378 {
00379 SET_PIXEL(picture, yuv_color, (i+x), (y+j));
00380 }
00381
00382 }
00383
00384
00385
00386
00387 void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts)
00388 {
00389 ContextInfo *ci = (ContextInfo *) ctx;
00390 FT_Face face = ci->face;
00391 FT_GlyphSlot slot = face->glyph;
00392 unsigned char *text = ci->text;
00393 unsigned char c;
00394 int x = 0, y = 0, i=0, size=0;
00395 unsigned char buff[MAXSIZE_TEXT];
00396 unsigned char tbuff[MAXSIZE_TEXT];
00397 time_t now = time(0);
00398 int str_w, str_w_max;
00399 FT_Vector pos[MAXSIZE_TEXT];
00400 FT_Vector delta;
00401
00402 if (ci->file)
00403 {
00404 int fd = open(ci->file, O_RDONLY);
00405
00406 if (fd < 0)
00407 {
00408 text = ci->text;
00409 av_log(NULL, AV_LOG_INFO, "WARNING: The file could not be opened. Using text provided with -t switch: %s", strerror(errno));
00410 }
00411 else
00412 {
00413 int l = read(fd, tbuff, sizeof(tbuff) - 1);
00414
00415 if (l >= 0)
00416 {
00417 tbuff[l] = 0;
00418 text = tbuff;
00419 }
00420 else
00421 {
00422 text = ci->text;
00423 av_log(NULL, AV_LOG_INFO, "WARNING: The file could not be read. Using text provided with -t switch: %s", strerror(errno));
00424 }
00425 close(fd);
00426 }
00427 }
00428 else
00429 {
00430 text = ci->text;
00431 }
00432
00433 strftime(buff, sizeof(buff), text, localtime(&now));
00434
00435 text = buff;
00436
00437 size = strlen(text);
00438
00439
00440
00441
00442
00443 str_w = str_w_max = 0;
00444 x = ci->x;
00445 y = ci->y;
00446 for (i=0; i < size; i++)
00447 {
00448 c = text[i];
00449
00450
00451 if ( (ci->use_kerning) && (i > 0) && (ci->glyphs_index[c]) )
00452 {
00453 FT_Get_Kerning( ci->face,
00454 ci->glyphs_index[ text[i-1] ],
00455 ci->glyphs_index[c],
00456 ft_kerning_default,
00457 &delta );
00458
00459 x += delta.x >> 6;
00460 }
00461
00462 if (( (x + ci->advance[ c ]) >= width ) || ( c == '\n' ))
00463 {
00464 str_w = width - ci->x - 1;
00465
00466 y += ci->text_height;
00467 x = ci->x;
00468 }
00469
00470
00471
00472 pos[i].x = x + ci->bitmap_left[c];
00473 pos[i].y = y - ci->bitmap_top[c] + ci->baseline;
00474
00475
00476 x += ci->advance[c];
00477
00478
00479 if (str_w > str_w_max)
00480 str_w_max = str_w;
00481
00482 }
00483
00484
00485
00486
00487 if (ci->bg)
00488 {
00489
00490 if ( str_w_max + ci->x >= width )
00491 str_w_max = width - ci->x - 1;
00492 if ( y >= height )
00493 y = height - 1 - 2*ci->y;
00494
00495
00496 draw_box( picture, ci->x, ci->y, str_w_max, y - ci->y, ci->bgcolor );
00497 }
00498
00499
00500
00501
00502 for (i=0; i < size; i++)
00503 {
00504 c = text[i];
00505
00506 if (
00507 ( (c == '_') && (text == ci->text) ) ||
00508
00509
00510 ( c == '\n' )
00511 )
00512 continue;
00513
00514
00515 draw_glyph( picture,
00516 &(ci->bitmaps[ c ]),
00517 pos[i].x,
00518 pos[i].y,
00519 width,
00520 height,
00521 ci->fgcolor,
00522 ci->bgcolor,
00523 ci->outline );
00524
00525
00526 x += slot->advance.x >> 6;
00527 }
00528
00529
00530 }
00531