av_free* cleanup
[libav.git] / vhook / watermark.c
CommitLineData
21754ce6 1/*
115329f1 2 * Watermark Hook
21754ce6
MN
3 * Copyright (c) 2005 Marcus Engene myfirstname(at)mylastname.se
4 *
ed818e25
ME
5 * flags to watermark:
6 * -m nbr = nbr is 0..1. 0 is the default mode, see below.
7 * -t nbr = nbr is six digit hex. Threshold.
8 * -f file = File is the filename of watermark image. You must specify this!
9 *
10 * MODE 0:
21754ce6
MN
11 * The watermarkpicture works like this. (Assuming colorintencities 0..0xff)
12 * Per color do this:
13 * If mask color is 0x80, no change to original frame.
14 * If mask color is < 0x80 the abs difference is subtracted from frame. If
15 * result < 0, result = 0
16 * If mask color is > 0x80 the abs difference is added to frame. If result
17 * > 0xff, result = 0xff
18 *
ed818e25
ME
19 * You can override the 0x80 level with the -t flag. Eg if threshold is 000000
20 * the color values of watermark is added to destination.
21 *
21754ce6 22 * This way a mask that is visible both in light pictures and in dark can be
115329f1 23 * made (fex by using a picture generated by gimp and the bump map tool).
21754ce6
MN
24 *
25 * An example watermark file is at
26 * http://engene.se/ffmpeg_watermark.gif
27 *
ed818e25
ME
28 * MODE 1:
29 * Per color do this:
30 * If mask color > threshold color, watermark pixel is going to be used.
31 *
ded78ac0 32 * Example usage:
ed818e25
ME
33 * ffmpeg -i infile -vhook '/path/watermark.so -f wm.gif' -an out.mov
34 * ffmpeg -i infile -vhook '/path/watermark.so -f wm.gif -m 1 -t 222222' -an out.mov
ded78ac0
ME
35 *
36 * Note that the entire vhook argument is encapsulated in ''. This
37 * way, arguments to the vhook won't be mixed up with those to ffmpeg.
38 *
21754ce6
MN
39 * This library is free software; you can redistribute it and/or
40 * modify it under the terms of the GNU Lesser General Public
41 * License as published by the Free Software Foundation; either
42 * version 2 of the License, or (at your option) any later version.
43 *
44 * This library is distributed in the hope that it will be useful,
45 * but WITHOUT ANY WARRANTY; without even the implied warranty of
46 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47 * Lesser General Public License for more details.
48 *
49 * You should have received a copy of the GNU Lesser General Public
50 * License along with this library; if not, write to the Free Software
5509bffa 51 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21754ce6
MN
52 */
53
ed818e25 54#include <stdlib.h>
21754ce6
MN
55//#include <fcntl.h>
56#include <unistd.h>
57#include <stdarg.h>
115329f1 58
21754ce6
MN
59#include "common.h"
60#include "avformat.h"
61
62#include "framehook.h"
a851b8e8 63#include "cmdutils.h"
21754ce6
MN
64
65typedef struct {
66 char filename[2000];
67 int x_size;
68 int y_size;
69
70 /* get_watermark_picture() variables */
71 AVFormatContext *pFormatCtx;
72 const char *p_ext;
73 int videoStream;
74 int frameFinished;
115329f1
DB
75 AVCodecContext *pCodecCtx;
76 AVCodec *pCodec;
21754ce6
MN
77 AVFrame *pFrame;
78 AVPacket packet;
79 int numBytes;
80 uint8_t *buffer;
81 int i;
82 AVInputFormat *file_iformat;
83 AVStream *st;
115329f1 84 int is_done;
21754ce6 85 AVFrame *pFrameRGB;
ed818e25
ME
86 int thrR;
87 int thrG;
88 int thrB;
89 int mode;
21754ce6
MN
90} ContextInfo;
91
92int get_watermark_picture(ContextInfo *ci, int cleanup);
93
94
95/****************************************************************************
115329f1 96 *
21754ce6
MN
97 ****************************************************************************/
98void Release(void *ctx)
99{
100 ContextInfo *ci;
101 ci = (ContextInfo *) ctx;
102
103 if (ci) get_watermark_picture(ci, 1);
115329f1 104
b4902c11 105 av_free(ctx);
21754ce6
MN
106}
107
108
109/****************************************************************************
115329f1 110 *
21754ce6
MN
111 ****************************************************************************/
112int Configure(void **ctxp, int argc, char *argv[])
113{
114 ContextInfo *ci;
115 int c;
ed818e25 116 int tmp = 0;
21754ce6
MN
117
118 if (0 == (*ctxp = av_mallocz(sizeof(ContextInfo)))) return -1;
119 ci = (ContextInfo *) *ctxp;
120
f368375c 121 optind = 1;
115329f1 122
21754ce6 123 // Struct is mallocz:ed so no need to reset.
ed818e25
ME
124 ci->thrR = 0x80;
125 ci->thrG = 0x80;
126 ci->thrB = 0x80;
115329f1 127
ed818e25 128 while ((c = getopt(argc, argv, "f:m:t:")) > 0) {
21754ce6
MN
129 switch (c) {
130 case 'f':
131 strncpy(ci->filename, optarg, 1999);
132 ci->filename[1999] = 0;
133 break;
ed818e25
ME
134 case 'm':
135 ci->mode = atoi(optarg);
136 break;
137 case 't':
138 if (1 != sscanf(optarg, "%x", &tmp)) {
139 av_log(NULL, AV_LOG_ERROR, "Watermark: argument to -t must be a 6 digit hex number\n");
140 return -1;
141 }
142 ci->thrR = (tmp >> 16) & 0xff;
143 ci->thrG = (tmp >> 8) & 0xff;
144 ci->thrB = (tmp >> 0) & 0xff;
145 break;
21754ce6 146 default:
f368375c 147 av_log(NULL, AV_LOG_ERROR, "Watermark: Unrecognized argument '%s'\n", argv[optind]);
21754ce6
MN
148 return -1;
149 }
150 }
115329f1 151
21754ce6 152 //
f368375c
ME
153 if (0 == ci->filename[0]) {
154 av_log(NULL, AV_LOG_ERROR, "Watermark: There is no filename specified.\n");
155 return -1;
156 }
115329f1 157
21754ce6
MN
158 av_register_all();
159 return get_watermark_picture(ci, 0);
21754ce6
MN
160}
161
162
163/****************************************************************************
ed818e25 164 * For mode 0 (the original one)
21754ce6 165 ****************************************************************************/
7b49ce2e 166static void Process0(void *ctx,
ed818e25
ME
167 AVPicture *picture,
168 enum PixelFormat pix_fmt,
169 int src_width,
170 int src_height,
171 int64_t pts)
21754ce6
MN
172{
173 ContextInfo *ci = (ContextInfo *) ctx;
174 char *buf = 0;
175 AVPicture picture1;
176 AVPicture *pict = picture;
115329f1 177
21754ce6
MN
178 AVFrame *pFrameRGB;
179 int xm_size;
180 int ym_size;
181
21754ce6
MN
182 int x;
183 int y;
184 int offs, offsm;
185 int mpoffs;
186 uint32_t *p_pixel = 0;
187 uint32_t pixel_meck;
188 uint32_t pixel;
189 uint32_t pixelm;
115329f1 190 int tmp;
ed818e25
ME
191 int thrR = ci->thrR;
192 int thrG = ci->thrG;
193 int thrB = ci->thrB;
21754ce6
MN
194
195 if (pix_fmt != PIX_FMT_RGBA32) {
196 int size;
197
198 size = avpicture_get_size(PIX_FMT_RGBA32, src_width, src_height);
199 buf = av_malloc(size);
200
201 avpicture_fill(&picture1, buf, PIX_FMT_RGBA32, src_width, src_height);
115329f1 202 if (img_convert(&picture1, PIX_FMT_RGBA32,
21754ce6
MN
203 picture, pix_fmt, src_width, src_height) < 0) {
204 av_free(buf);
205 return;
206 }
207 pict = &picture1;
208 }
209
210 /* Insert filter code here */ /* ok */
211
115329f1 212 // Get me next frame
21754ce6
MN
213 if (0 > get_watermark_picture(ci, 0)) {
214 return;
115329f1 215 }
21754ce6
MN
216 // These are the three original static variables in the ffmpeg hack.
217 pFrameRGB = ci->pFrameRGB;
218 xm_size = ci->x_size;
219 ym_size = ci->y_size;
115329f1 220
21754ce6
MN
221 // I'll do the *4 => <<2 crap later. Most compilers understand that anyway.
222 // According to avcodec.h PIX_FMT_RGBA32 is handled in endian specific manner.
223 for (y=0; y<src_height; y++) {
224 offs = y * (src_width * 4);
225 offsm = (((y * ym_size) / src_height) * 4) * xm_size; // offsm first in maskline. byteoffs!
226 for (x=0; x<src_width; x++) {
227 mpoffs = offsm + (((x * xm_size) / src_width) * 4);
228 p_pixel = (uint32_t *)&((pFrameRGB->data[0])[mpoffs]);
229 pixelm = *p_pixel;
230 p_pixel = (uint32_t *)&((pict->data[0])[offs]);
231 pixel = *p_pixel;
232// pixelm = *((uint32_t *)&(pFrameRGB->data[mpoffs]));
233 pixel_meck = pixel & 0xff000000;
234
235 // R
ed818e25 236 tmp = (int)((pixel >> 16) & 0xff) + (int)((pixelm >> 16) & 0xff) - thrR;
21754ce6
MN
237 if (tmp > 255) tmp = 255;
238 if (tmp < 0) tmp = 0;
239 pixel_meck |= (tmp << 16) & 0xff0000;
240 // G
ed818e25 241 tmp = (int)((pixel >> 8) & 0xff) + (int)((pixelm >> 8) & 0xff) - thrG;
21754ce6
MN
242 if (tmp > 255) tmp = 255;
243 if (tmp < 0) tmp = 0;
244 pixel_meck |= (tmp << 8) & 0xff00;
245 // B
ed818e25 246 tmp = (int)((pixel >> 0) & 0xff) + (int)((pixelm >> 0) & 0xff) - thrB;
21754ce6
MN
247 if (tmp > 255) tmp = 255;
248 if (tmp < 0) tmp = 0;
249 pixel_meck |= (tmp << 0) & 0xff;
115329f1
DB
250
251
21754ce6
MN
252 // test:
253 //pixel_meck = pixel & 0xff000000;
254 //pixel_meck |= (pixelm & 0x00ffffff);
255
256 *p_pixel = pixel_meck;
257
115329f1 258 offs += 4;
21754ce6 259 } // foreach X
115329f1
DB
260 } // foreach Y
261
262
263
21754ce6
MN
264
265 if (pix_fmt != PIX_FMT_RGBA32) {
115329f1 266 if (img_convert(picture, pix_fmt,
21754ce6
MN
267 &picture1, PIX_FMT_RGBA32, src_width, src_height) < 0) {
268 }
269 }
270
271 av_free(buf);
272}
273
274
275/****************************************************************************
ed818e25
ME
276 * For mode 1 (the original one)
277 ****************************************************************************/
7b49ce2e 278static void Process1(void *ctx,
ed818e25
ME
279 AVPicture *picture,
280 enum PixelFormat pix_fmt,
281 int src_width,
282 int src_height,
283 int64_t pts)
284{
285 ContextInfo *ci = (ContextInfo *) ctx;
286 char *buf = 0;
287 AVPicture picture1;
288 AVPicture *pict = picture;
289
290 AVFrame *pFrameRGB;
291 int xm_size;
292 int ym_size;
293
294 int x;
295 int y;
296 int offs, offsm;
297 int mpoffs;
298 uint32_t *p_pixel = 0;
299 uint32_t pixel;
300 uint32_t pixelm;
301
302 if (pix_fmt != PIX_FMT_RGBA32) {
303 int size;
304
305 size = avpicture_get_size(PIX_FMT_RGBA32, src_width, src_height);
306 buf = av_malloc(size);
307
308 avpicture_fill(&picture1, buf, PIX_FMT_RGBA32, src_width, src_height);
309 if (img_convert(&picture1, PIX_FMT_RGBA32,
310 picture, pix_fmt, src_width, src_height) < 0) {
311 av_free(buf);
312 return;
313 }
314 pict = &picture1;
315 }
316
317 /* Insert filter code here */ /* ok */
318
319 // Get me next frame
320 if (0 > get_watermark_picture(ci, 0)) {
321 return;
322 }
323 // These are the three original static variables in the ffmpeg hack.
324 pFrameRGB = ci->pFrameRGB;
325 xm_size = ci->x_size;
326 ym_size = ci->y_size;
327
328 // I'll do the *4 => <<2 crap later. Most compilers understand that anyway.
329 // According to avcodec.h PIX_FMT_RGBA32 is handled in endian specific manner.
330 for (y=0; y<src_height; y++) {
331 offs = y * (src_width * 4);
332 offsm = (((y * ym_size) / src_height) * 4) * xm_size; // offsm first in maskline. byteoffs!
333 for (x=0; x<src_width; x++) {
334 mpoffs = offsm + (((x * xm_size) / src_width) * 4);
335 p_pixel = (uint32_t *)&((pFrameRGB->data[0])[mpoffs]);
336 pixelm = *p_pixel; /* watermark pixel */
337 p_pixel = (uint32_t *)&((pict->data[0])[offs]);
338 pixel = *p_pixel;
339
340 if (((pixelm >> 16) & 0xff) > ci->thrR ||
341 ((pixelm >> 8) & 0xff) > ci->thrG ||
342 ((pixelm >> 0) & 0xff) > ci->thrB)
343 {
344 *p_pixel = pixelm;
345 } else {
346 *p_pixel = pixel;
347 }
348 offs += 4;
349 } // foreach X
350 } // foreach Y
351
352 if (pix_fmt != PIX_FMT_RGBA32) {
353 if (img_convert(picture, pix_fmt,
354 &picture1, PIX_FMT_RGBA32, src_width, src_height) < 0) {
355 }
356 }
357
358 av_free(buf);
359}
360
361
362/****************************************************************************
363 * This is the function ffmpeg.c callbacks.
364 ****************************************************************************/
365void Process(void *ctx,
366 AVPicture *picture,
367 enum PixelFormat pix_fmt,
368 int src_width,
369 int src_height,
370 int64_t pts)
371{
372 ContextInfo *ci = (ContextInfo *) ctx;
373 if (1 == ci->mode) {
374 return Process1(ctx, picture, pix_fmt, src_width, src_height, pts);
375 } else {
376 return Process0(ctx, picture, pix_fmt, src_width, src_height, pts);
377 }
378}
379
380
381/****************************************************************************
21754ce6
MN
382 * When cleanup == 0, we try to get the next frame. If no next frame, nothing
383 * is done.
384 *
115329f1 385 * This code follows the example on
21754ce6
MN
386 * http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html
387 *
388 * 0 = ok, -1 = error
389 ****************************************************************************/
390int get_watermark_picture(ContextInfo *ci, int cleanup)
391{
392 if (1 == ci->is_done && 0 == cleanup) return 0;
393
394 // Yes, *pFrameRGB arguments must be null the first time otherwise it's not good..
395 // This block is only executed the first time we enter this function.
115329f1
DB
396 if (0 == ci->pFrameRGB &&
397 0 == cleanup)
21754ce6 398 {
115329f1
DB
399
400 /*
21754ce6
MN
401 * The last three parameters specify the file format, buffer size and format
402 * parameters; by simply specifying NULL or 0 we ask libavformat to auto-detect
403 * the format and use a default buffer size. (Didn't work!)
404 */
405 if (av_open_input_file(&ci->pFormatCtx, ci->filename, NULL, 0, NULL) != 0) {
406
407 // Martin says this should not be necessary but it failed for me sending in
408 // NULL instead of file_iformat to av_open_input_file()
409 ci->i = strlen(ci->filename);
410 if (0 == ci->i) {
f368375c 411 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() No filename to watermark vhook\n");
21754ce6
MN
412 return -1;
413 }
414 while (ci->i > 0) {
415 if (ci->filename[ci->i] == '.') {
416 ci->i++;
417 break;
418 }
419 ci->i--;
420 }
421 ci->p_ext = &(ci->filename[ci->i]);
422 ci->file_iformat = av_find_input_format (ci->p_ext);
423 if (0 == ci->file_iformat) {
f368375c 424 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Really failed to find iformat [%s]\n", ci->p_ext);
21754ce6
MN
425 return -1;
426 }
115329f1
DB
427 // now continues the Martin template.
428
21754ce6 429 if (av_open_input_file(&ci->pFormatCtx, ci->filename, ci->file_iformat, 0, NULL)!=0) {
f368375c 430 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to open input file [%s]\n", ci->filename);
21754ce6 431 return -1;
115329f1 432 }
21754ce6 433 }
115329f1
DB
434
435 /*
21754ce6
MN
436 * This fills the streams field of the AVFormatContext with valid information.
437 */
438 if(av_find_stream_info(ci->pFormatCtx)<0) {
f368375c 439 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find stream info\n");
21754ce6
MN
440 return -1;
441 }
115329f1 442
21754ce6 443 /*
115329f1 444 * As mentioned in the introduction, we'll handle only video streams, not audio
21754ce6
MN
445 * streams. To make things nice and easy, we simply use the first video stream we
446 * find.
447 */
448 ci->videoStream=-1;
449 for(ci->i = 0; ci->i < ci->pFormatCtx->nb_streams; ci->i++)
a2cfc4d6 450 if(ci->pFormatCtx->streams[ci->i]->codec->codec_type==CODEC_TYPE_VIDEO)
21754ce6
MN
451 {
452 ci->videoStream = ci->i;
453 break;
454 }
455 if(ci->videoStream == -1) {
f368375c 456 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find any video stream\n");
21754ce6
MN
457 return -1;
458 }
115329f1 459
21754ce6 460 ci->st = ci->pFormatCtx->streams[ci->videoStream];
a2cfc4d6
MM
461 ci->x_size = ci->st->codec->width;
462 ci->y_size = ci->st->codec->height;
115329f1 463
21754ce6 464 // Get a pointer to the codec context for the video stream
a2cfc4d6 465 ci->pCodecCtx = ci->pFormatCtx->streams[ci->videoStream]->codec;
115329f1
DB
466
467
21754ce6
MN
468 /*
469 * OK, so now we've got a pointer to the so-called codec context for our video
470 * stream, but we still have to find the actual codec and open it.
115329f1 471 */
21754ce6
MN
472 // Find the decoder for the video stream
473 ci->pCodec = avcodec_find_decoder(ci->pCodecCtx->codec_id);
474 if(ci->pCodec == NULL) {
f368375c 475 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find any codec\n");
21754ce6
MN
476 return -1;
477 }
115329f1 478
21754ce6
MN
479 // Inform the codec that we can handle truncated bitstreams -- i.e.,
480 // bitstreams where frame boundaries can fall in the middle of packets
481 if (ci->pCodec->capabilities & CODEC_CAP_TRUNCATED)
482 ci->pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;
115329f1 483
21754ce6
MN
484 // Open codec
485 if(avcodec_open(ci->pCodecCtx, ci->pCodec)<0) {
f368375c 486 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to open codec\n");
21754ce6
MN
487 return -1;
488 }
115329f1
DB
489
490 // Hack to correct wrong frame rates that seem to be generated by some
21754ce6 491 // codecs
c0df9d75 492 if (ci->pCodecCtx->time_base.den>1000 && ci->pCodecCtx->time_base.num==1)
115329f1
DB
493 ci->pCodecCtx->time_base.num=1000;
494
21754ce6
MN
495 /*
496 * Allocate a video frame to store the decoded images in.
497 */
498 ci->pFrame = avcodec_alloc_frame();
115329f1
DB
499
500
21754ce6
MN
501 /*
502 * The RGB image pFrameRGB (of type AVFrame *) is allocated like this:
503 */
504 // Allocate an AVFrame structure
505 ci->pFrameRGB=avcodec_alloc_frame();
506 if(ci->pFrameRGB==NULL) {
f368375c 507 av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to alloc pFrameRGB\n");
21754ce6
MN
508 return -1;
509 }
115329f1 510
21754ce6
MN
511 // Determine required buffer size and allocate buffer
512 ci->numBytes = avpicture_get_size(PIX_FMT_RGBA32, ci->pCodecCtx->width,
513 ci->pCodecCtx->height);
514 ci->buffer = av_malloc(ci->numBytes);
115329f1 515
21754ce6
MN
516 // Assign appropriate parts of buffer to image planes in pFrameRGB
517 avpicture_fill((AVPicture *)ci->pFrameRGB, ci->buffer, PIX_FMT_RGBA32,
115329f1 518 ci->pCodecCtx->width, ci->pCodecCtx->height);
21754ce6
MN
519 }
520 // TODO loop, pingpong etc?
115329f1
DB
521 if (0 == cleanup)
522 {
21754ce6
MN
523// av_log(NULL, AV_LOG_DEBUG, "get_watermark_picture() Get a frame\n");
524 while(av_read_frame(ci->pFormatCtx, &ci->packet)>=0)
525 {
526 // Is this a packet from the video stream?
527 if(ci->packet.stream_index == ci->videoStream)
528 {
529 // Decode video frame
115329f1 530 avcodec_decode_video(ci->pCodecCtx, ci->pFrame, &ci->frameFinished,
21754ce6 531 ci->packet.data, ci->packet.size);
115329f1 532
21754ce6
MN
533 // Did we get a video frame?
534 if(ci->frameFinished)
535 {
536 // Convert the image from its native format to RGBA32
115329f1
DB
537 img_convert((AVPicture *)ci->pFrameRGB, PIX_FMT_RGBA32,
538 (AVPicture*)(ci->pFrame), ci->pCodecCtx->pix_fmt, ci->pCodecCtx->width,
21754ce6 539 ci->pCodecCtx->height);
115329f1 540
21754ce6
MN
541 // Process the video frame (save to disk etc.)
542 //fprintf(stderr,"banan() New frame!\n");
543 //DoSomethingWithTheImage(ci->pFrameRGB);
544 return 0;
545 }
546 }
115329f1 547
21754ce6
MN
548 // Free the packet that was allocated by av_read_frame
549 av_free_packet(&ci->packet);
550 }
115329f1 551 ci->is_done = 1;
21754ce6
MN
552 return 0;
553 } // if 0 != cleanup
115329f1
DB
554
555 if (0 != cleanup)
21754ce6
MN
556 {
557 // Free the RGB image
b4902c11
MN
558 av_freep(&ci->buffer);
559 av_freep(&ci->pFrameRGB);
115329f1 560
21754ce6
MN
561 // Close the codec
562 if (0 != ci->pCodecCtx) {
563 avcodec_close(ci->pCodecCtx);
564 ci->pCodecCtx = 0;
565 }
115329f1 566
21754ce6
MN
567 // Close the video file
568 if (0 != ci->pFormatCtx) {
115329f1 569 av_close_input_file(ci->pFormatCtx);
21754ce6
MN
570 ci->pFormatCtx = 0;
571 }
115329f1 572
21754ce6
MN
573 ci->is_done = 0;
574 }
575 return 0;
576}
577
578
579void parse_arg_file(const char *filename)
580{
581}