Statistics
| Branch: | Revision:

ffmpeg / vhook / watermark.c @ dd933153

History | View | Annotate | Download (18.9 KB)

1
/*
2
 * Watermark Hook
3
 * Copyright (c) 2005 Marcus Engene myfirstname(at)mylastname.se
4
 *
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:
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
 *
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
 *
22
 * This way a mask that is visible both in light pictures and in dark can be
23
 * made (fex by using a picture generated by gimp and the bump map tool).
24
 *
25
 * An example watermark file is at
26
 * http://engene.se/ffmpeg_watermark.gif
27
 *
28
 * MODE 1:
29
 * Per color do this:
30
 * If mask color > threshold color, watermark pixel is going to be used.
31
 *
32
 * Example usage:
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
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
 *
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
51
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
52
 */
53

    
54
#include <stdlib.h>
55
//#include <fcntl.h>
56
#include <unistd.h>
57
#include <stdarg.h>
58

    
59
#include "common.h"
60
#include "avformat.h"
61

    
62
#include "framehook.h"
63
#include "cmdutils.h"
64

    
65
typedef 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;
75
    AVCodecContext *pCodecCtx;
76
    AVCodec        *pCodec;
77
    AVFrame        *pFrame;
78
    AVPacket        packet;
79
    int             numBytes;
80
    uint8_t        *buffer;
81
    int             i;
82
    AVInputFormat  *file_iformat;
83
    AVStream       *st;
84
    int             is_done;
85
    AVFrame        *pFrameRGB;
86
    int             thrR;
87
    int             thrG;
88
    int             thrB;
89
    int             mode;
90
} ContextInfo;
91

    
92
int get_watermark_picture(ContextInfo *ci, int cleanup);
93

    
94

    
95
/****************************************************************************
96
 *
97
 ****************************************************************************/
98
void Release(void *ctx)
99
{
100
    ContextInfo *ci;
101
    ci = (ContextInfo *) ctx;
102

    
103
    if (ci) get_watermark_picture(ci, 1);
104

    
105
    av_free(ctx);
106
}
107

    
108

    
109
/****************************************************************************
110
 *
111
 ****************************************************************************/
112
int Configure(void **ctxp, int argc, char *argv[])
113
{
114
    ContextInfo *ci;
115
    int c;
116
    int tmp = 0;
117

    
118
    if (0 == (*ctxp = av_mallocz(sizeof(ContextInfo)))) return -1;
119
    ci = (ContextInfo *) *ctxp;
120

    
121
    optind = 1;
122

    
123
    // Struct is mallocz:ed so no need to reset.
124
    ci->thrR = 0x80;
125
    ci->thrG = 0x80;
126
    ci->thrB = 0x80;
127

    
128
    while ((c = getopt(argc, argv, "f:m:t:")) > 0) {
129
        switch (c) {
130
            case 'f':
131
                strncpy(ci->filename, optarg, 1999);
132
                ci->filename[1999] = 0;
133
                break;
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;
146
            default:
147
                av_log(NULL, AV_LOG_ERROR, "Watermark: Unrecognized argument '%s'\n", argv[optind]);
148
                return -1;
149
        }
150
    }
151

    
152
    //
153
    if (0 == ci->filename[0]) {
154
        av_log(NULL, AV_LOG_ERROR, "Watermark: There is no filename specified.\n");
155
        return -1;
156
    }
157

    
158
    av_register_all();
159
    return get_watermark_picture(ci, 0);
160
}
161

    
162

    
163
/****************************************************************************
164
 * For mode 0 (the original one)
165
 ****************************************************************************/
166
static void Process0(void *ctx,
167
              AVPicture *picture,
168
              enum PixelFormat pix_fmt,
169
              int src_width,
170
              int src_height,
171
              int64_t pts)
172
{
173
    ContextInfo *ci = (ContextInfo *) ctx;
174
    char *buf = 0;
175
    AVPicture picture1;
176
    AVPicture *pict = picture;
177

    
178
    AVFrame *pFrameRGB;
179
    int xm_size;
180
    int ym_size;
181

    
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;
190
    int tmp;
191
    int thrR = ci->thrR;
192
    int thrG = ci->thrG;
193
    int thrB = ci->thrB;
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);
202
        if (img_convert(&picture1, PIX_FMT_RGBA32,
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

    
212
    // Get me next frame
213
    if (0 > get_watermark_picture(ci, 0)) {
214
        return;
215
    }
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;
220

    
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
236
            tmp = (int)((pixel >> 16) & 0xff) + (int)((pixelm >> 16) & 0xff) - thrR;
237
            if (tmp > 255) tmp = 255;
238
            if (tmp < 0) tmp = 0;
239
            pixel_meck |= (tmp << 16) & 0xff0000;
240
            // G
241
            tmp = (int)((pixel >> 8) & 0xff) + (int)((pixelm >> 8) & 0xff) - thrG;
242
            if (tmp > 255) tmp = 255;
243
            if (tmp < 0) tmp = 0;
244
            pixel_meck |= (tmp << 8) & 0xff00;
245
            // B
246
            tmp = (int)((pixel >> 0) & 0xff) + (int)((pixelm >> 0) & 0xff) - thrB;
247
            if (tmp > 255) tmp = 255;
248
            if (tmp < 0) tmp = 0;
249
            pixel_meck |= (tmp << 0) & 0xff;
250

    
251

    
252
            // test:
253
            //pixel_meck = pixel & 0xff000000;
254
            //pixel_meck |= (pixelm & 0x00ffffff);
255

    
256
            *p_pixel = pixel_meck;
257

    
258
            offs += 4;
259
        } // foreach X
260
    } // foreach Y
261

    
262

    
263

    
264

    
265
    if (pix_fmt != PIX_FMT_RGBA32) {
266
        if (img_convert(picture, pix_fmt,
267
                        &picture1, PIX_FMT_RGBA32, src_width, src_height) < 0) {
268
        }
269
    }
270

    
271
    av_free(buf);
272
}
273

    
274

    
275
/****************************************************************************
276
 * For mode 1 (the original one)
277
 ****************************************************************************/
278
static void Process1(void *ctx,
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
 ****************************************************************************/
365
void 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
/****************************************************************************
382
 * When cleanup == 0, we try to get the next frame. If no next frame, nothing
383
 * is done.
384
 *
385
 * This code follows the example on
386
 * http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html
387
 *
388
 * 0 = ok, -1 = error
389
 ****************************************************************************/
390
int 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.
396
    if (0 == ci->pFrameRGB &&
397
        0 == cleanup)
398
    {
399

    
400
        /*
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) {
411
                av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() No filename to watermark vhook\n");
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) {
424
                av_log(NULL, AV_LOG_INFO, "get_watermark_picture() attempt to use image2 for [%s]\n", ci->p_ext);
425
                ci->file_iformat = av_find_input_format ("image2");
426
            }
427
            if (0 == ci->file_iformat) {
428
                av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Really failed to find iformat [%s]\n", ci->p_ext);
429
                return -1;
430
            }
431
            // now continues the Martin template.
432

    
433
            if (av_open_input_file(&ci->pFormatCtx, ci->filename, ci->file_iformat, 0, NULL)!=0) {
434
                av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to open input file [%s]\n", ci->filename);
435
                return -1;
436
            }
437
        }
438

    
439
        /*
440
         * This fills the streams field of the AVFormatContext with valid information.
441
         */
442
        if(av_find_stream_info(ci->pFormatCtx)<0) {
443
            av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find stream info\n");
444
            return -1;
445
        }
446

    
447
        /*
448
         * As mentioned in the introduction, we'll handle only video streams, not audio
449
         * streams. To make things nice and easy, we simply use the first video stream we
450
         * find.
451
         */
452
        ci->videoStream=-1;
453
        for(ci->i = 0; ci->i < ci->pFormatCtx->nb_streams; ci->i++)
454
            if(ci->pFormatCtx->streams[ci->i]->codec->codec_type==CODEC_TYPE_VIDEO)
455
            {
456
                ci->videoStream = ci->i;
457
                break;
458
            }
459
        if(ci->videoStream == -1) {
460
            av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find any video stream\n");
461
            return -1;
462
        }
463

    
464
        ci->st = ci->pFormatCtx->streams[ci->videoStream];
465
        ci->x_size = ci->st->codec->width;
466
        ci->y_size = ci->st->codec->height;
467

    
468
        // Get a pointer to the codec context for the video stream
469
        ci->pCodecCtx = ci->pFormatCtx->streams[ci->videoStream]->codec;
470

    
471

    
472
        /*
473
         * OK, so now we've got a pointer to the so-called codec context for our video
474
         * stream, but we still have to find the actual codec and open it.
475
         */
476
        // Find the decoder for the video stream
477
        ci->pCodec = avcodec_find_decoder(ci->pCodecCtx->codec_id);
478
        if(ci->pCodec == NULL) {
479
            av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find any codec\n");
480
            return -1;
481
        }
482

    
483
        // Inform the codec that we can handle truncated bitstreams -- i.e.,
484
        // bitstreams where frame boundaries can fall in the middle of packets
485
        if (ci->pCodec->capabilities & CODEC_CAP_TRUNCATED)
486
            ci->pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;
487

    
488
        // Open codec
489
        if(avcodec_open(ci->pCodecCtx, ci->pCodec)<0) {
490
            av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to open codec\n");
491
            return -1;
492
        }
493

    
494
        // Hack to correct wrong frame rates that seem to be generated by some
495
        // codecs
496
        if (ci->pCodecCtx->time_base.den>1000 && ci->pCodecCtx->time_base.num==1)
497
            ci->pCodecCtx->time_base.num=1000;
498

    
499
        /*
500
         * Allocate a video frame to store the decoded images in.
501
         */
502
        ci->pFrame = avcodec_alloc_frame();
503

    
504

    
505
        /*
506
         * The RGB image pFrameRGB (of type AVFrame *) is allocated like this:
507
         */
508
        // Allocate an AVFrame structure
509
        ci->pFrameRGB=avcodec_alloc_frame();
510
        if(ci->pFrameRGB==NULL) {
511
            av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to alloc pFrameRGB\n");
512
            return -1;
513
        }
514

    
515
        // Determine required buffer size and allocate buffer
516
        ci->numBytes = avpicture_get_size(PIX_FMT_RGBA32, ci->pCodecCtx->width,
517
            ci->pCodecCtx->height);
518
        ci->buffer = av_malloc(ci->numBytes);
519

    
520
        // Assign appropriate parts of buffer to image planes in pFrameRGB
521
        avpicture_fill((AVPicture *)ci->pFrameRGB, ci->buffer, PIX_FMT_RGBA32,
522
            ci->pCodecCtx->width, ci->pCodecCtx->height);
523
    }
524
    // TODO loop, pingpong etc?
525
    if (0 == cleanup)
526
    {
527
//        av_log(NULL, AV_LOG_DEBUG, "get_watermark_picture() Get a frame\n");
528
        while(av_read_frame(ci->pFormatCtx, &ci->packet)>=0)
529
        {
530
            // Is this a packet from the video stream?
531
            if(ci->packet.stream_index == ci->videoStream)
532
            {
533
                // Decode video frame
534
                avcodec_decode_video(ci->pCodecCtx, ci->pFrame, &ci->frameFinished,
535
                    ci->packet.data, ci->packet.size);
536

    
537
                // Did we get a video frame?
538
                if(ci->frameFinished)
539
                {
540
                    // Convert the image from its native format to RGBA32
541
                    img_convert((AVPicture *)ci->pFrameRGB, PIX_FMT_RGBA32,
542
                        (AVPicture*)(ci->pFrame), ci->pCodecCtx->pix_fmt, ci->pCodecCtx->width,
543
                        ci->pCodecCtx->height);
544

    
545
                    // Process the video frame (save to disk etc.)
546
                    //fprintf(stderr,"banan() New frame!\n");
547
                    //DoSomethingWithTheImage(ci->pFrameRGB);
548
                    return 0;
549
                }
550
            }
551

    
552
            // Free the packet that was allocated by av_read_frame
553
            av_free_packet(&ci->packet);
554
        }
555
        ci->is_done = 1;
556
        return 0;
557
    } // if 0 != cleanup
558

    
559
    if (0 != cleanup)
560
    {
561
        // Free the RGB image
562
        av_freep(&ci->buffer);
563
        av_freep(&ci->pFrameRGB);
564

    
565
        // Close the codec
566
        if (0 != ci->pCodecCtx) {
567
            avcodec_close(ci->pCodecCtx);
568
            ci->pCodecCtx = 0;
569
        }
570

    
571
        // Close the video file
572
        if (0 != ci->pFormatCtx) {
573
            av_close_input_file(ci->pFormatCtx);
574
            ci->pFormatCtx = 0;
575
        }
576

    
577
        ci->is_done = 0;
578
    }
579
    return 0;
580
}
581

    
582

    
583
void parse_arg_file(const char *filename)
584
{
585
}