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