1 /*
2  *  filter_pv.c
3  *
4  *  Copyright (C) Thomas Oestreich - October 2002
5  *
6  *  This file is part of transcode, a video stream processing tool
7  *
8  *  transcode is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2, or (at your option)
11  *  any later version.
12  *
13  *  transcode is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with GNU Make; see the file COPYING.  If not, write to
20  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  */
23 
24 #define MOD_NAME    "filter_pv.so"
25 #define MOD_VERSION "v0.2.3 (2004-06-01)"
26 #define MOD_CAP     "xv only preview plugin"
27 #define MOD_AUTHOR  "Thomas Oestreich, Tilmann Bitterberg"
28 
29 #include "transcode.h"
30 #include "filter.h"
31 #include "libtc/optstr.h"
32 #include "libtcvideo/tcvideo.h"
33 
34 #include "video_trans.h"
35 
36 #ifdef HAVE_DLFCN_H
37 #include <dlfcn.h>
38 #else
39 # ifdef OS_DARWIN
40 #  include "libdldarwin/dlfcn.h"
41 # endif
42 #endif
43 
44 #include "pv.h"
45 #include "font_xpm.h"
46 
47 
48 static int cache_num=0;
49 static int cache_ptr=0;
50 static int cache_enabled=0;
51 
52 //cache navigation
53 int cache_long_skip  = 25;
54 int cache_short_skip =  1;
55 
56 static char *vid_buf_mem=NULL;
57 static char **vid_buf=NULL;
58 
59 static int w, h;
60 
61 static int cols=20;
62 static int rows=20;
63 
64 static char buffer[128];
65 static int size=0;
66 static int use_secondary_buffer=0;
67 static ImageFormat srcfmt = IMG_NONE, destfmt = IMG_NONE;
68 
69 static int xv_init_ok=0;
70 
71 static int preview_delay=0;
72 static int preview_skip=0, preview_skip_num=25;
73 static int preview_xv_port=0;
74 static char *undo_buffer = NULL;
75 static char *run_buffer[2] = {NULL, NULL};
76 static char *process_buffer[3] = {NULL, NULL, NULL};
77 
78 static int process_ctr_cur=0;
79 
80 static TCVHandle tcvhandle;
81 
82 
83 /* global variables */
84 
85 static xv_player_t *xv_player = NULL;
86 
87 
88 #define ONE_SECOND 1000000 // in units of usec
89 
inc_preview_delay(void)90 void inc_preview_delay(void)
91 {
92   preview_delay+=ONE_SECOND/10;
93   if(preview_delay>ONE_SECOND) preview_delay=ONE_SECOND;
94 }
95 
dec_preview_delay(void)96 void dec_preview_delay(void)
97 {
98   preview_delay-=ONE_SECOND/10;
99   if(preview_delay<0) preview_delay=0;
100 }
101 
preview_toggle_skip(void)102 void preview_toggle_skip(void)
103 {
104   preview_skip = (preview_skip>0) ? 0: preview_skip_num;
105 }
106 
107 /*-------------------------------------------------
108  *
109  * single function interface
110  *
111  *-------------------------------------------------*/
112 
tc_filter(frame_list_t * ptr_,char * options)113 int tc_filter(frame_list_t *ptr_, char *options)
114 {
115   vframe_list_t *ptr = (vframe_list_t *)ptr_;
116   vob_t *vob=NULL;
117 
118   int pre=0, vid=0;
119 
120   if(ptr->tag & TC_FILTER_GET_CONFIG) {
121       optstr_filter_desc (options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VY4O", "1");
122       optstr_param (options, "cache", "Number of raw frames to cache for seeking",  "%d", "15", "15", "255");
123       optstr_param (options, "skip", "display only every Nth frame",  "%d", "0", "0", "255");
124       optstr_param (options, "fullscreen", "Display in fullscreen mode","", "0");
125       optstr_param (options, "port", "force Xv port","%d", "0", "0", "255");
126   }
127 
128   //----------------------------------
129   //
130   // filter init
131   //
132   //----------------------------------
133 
134   if(ptr->tag & TC_FILTER_INIT) {
135 
136     if((vob = tc_get_vob())==NULL) return(-1);
137 
138     // filter init ok.
139 
140     if(verbose) tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
141 
142     if (options != NULL) {
143 
144       if(verbose) tc_log_info(MOD_NAME, "options=%s", options);
145 
146       optstr_get (options, "cache", "%d", &cache_num);
147 
148       //adjust for small buffers
149       if(cache_num && cache_num<15) {
150 	cache_num=15;
151 	cache_long_skip=5;
152       }
153 
154       optstr_get (options, "skip", "%d", &preview_skip_num);
155       if (optstr_lookup(options, "help")) return -1;
156 
157     }
158 
159     if(cache_num<0) tc_log_warn(MOD_NAME, "invalid cache number - exit");
160     if(preview_skip_num<0) tc_log_warn(MOD_NAME, "invalid number of frames to skip - exit");
161 
162     tc_snprintf(buffer, sizeof(buffer), "%s-%s", PACKAGE, VERSION);
163 
164     if(xv_player != NULL) return(-1);
165     if(!(xv_player = xv_player_new())) return(-1);
166 
167 
168     if (options != NULL) {
169       if(optstr_lookup(options, "fullscreen") != NULL)
170         xv_player->display->full_screen = 1;
171       optstr_get (options, "port", "%d", &preview_xv_port);
172       if(preview_xv_port != 0) {
173         tc_log_info(MOD_NAME, "forced Xv port: %d", preview_xv_port);
174         xv_player->display->arg_xv_port = preview_xv_port;
175       }
176     }
177 
178 
179     //init filter
180 
181     w = vob->ex_v_width;
182     h = vob->ex_v_height;
183 
184     size = w*h* 3/2;
185 
186     if(verbose) tc_log_info(MOD_NAME, "preview window %dx%d", w, h);
187 
188     tcvhandle = tcv_init();
189     if (!tcvhandle) {
190       tc_log_error(MOD_NAME, "tcv_init() failed");
191       return -1;
192     }
193 
194     switch(vob->im_v_codec) {
195 
196     case CODEC_YUV422:
197 
198       if(xv_display_init(xv_player->display, 0, NULL,
199 			 w, h, buffer, buffer, 1)<0) return(-1);
200       size = w*h*2;
201       srcfmt = IMG_YUV422P;
202       destfmt = IMG_YUY2;
203 
204       break;
205 
206     case CODEC_YUV:
207 
208       if(xv_display_init(xv_player->display, 0, NULL,
209 			 w, h, buffer, buffer, 0)<0) return(-1);
210 
211       break;
212 
213     case CODEC_RAW_YUV:
214 
215       if(xv_display_init(xv_player->display, 0, NULL,
216 			  w, h, buffer, buffer, 0)<0) return(-1);
217       use_secondary_buffer=1;
218       break;
219 
220     default:
221       tc_log_error(MOD_NAME, "non-YUV codecs not supported for this preview plug-in");
222       return(-1);
223     }
224 
225     //cache
226 
227     if (cache_num) {
228       if(preview_cache_init()<0) return(-1);
229 
230       /* FIXME: these are never freed! */
231       if ((undo_buffer = tc_bufalloc(SIZE_RGB_FRAME)) == NULL)
232 	  return (-1);
233       if ((run_buffer[0] = tc_bufalloc (SIZE_RGB_FRAME)) == NULL)
234 	  return (-1);
235       if ((run_buffer[1] = tc_bufalloc (SIZE_RGB_FRAME)) == NULL)
236 	  return (-1);
237       if ((process_buffer[0] = tc_bufalloc (SIZE_RGB_FRAME)) == NULL)
238 	  return (-1);
239       if ((process_buffer[1] = tc_bufalloc (SIZE_RGB_FRAME)) == NULL)
240 	  return (-1);
241       if ((process_buffer[2] = tc_bufalloc (SIZE_RGB_FRAME)) == NULL)
242 	  return (-1);
243 
244     }
245 
246     xv_init_ok=1;
247 
248     return(0);
249   }
250 
251   //----------------------------------
252   //
253   // filter close
254   //
255   //----------------------------------
256 
257   if(ptr->tag & TC_FILTER_CLOSE) {
258 
259     if(!xv_init_ok) return(0);
260 
261     if(size) xv_display_exit(xv_player->display);
262 
263     tcv_free(tcvhandle);
264     tcvhandle = 0;
265 
266     return(0);
267   }
268 
269   //----------------------------------
270   //
271   // filter frame routine
272   //
273   //----------------------------------
274 
275   // tag variable indicates, if we are called before
276   // transcodes internal video/audo frame processing routines
277   // or after and determines video/audio context
278 
279   if(verbose & TC_STATS) tc_log_info(MOD_NAME, "%s/%s %s %s", vob->mod_path, MOD_NAME, MOD_VERSION, MOD_CAP);
280 
281   //we do nothing if not properly initialized
282   if(!xv_init_ok) return(0);
283 
284   // tag variable indicates, if we are called before
285   // transcodes internal video/audo frame processing routines
286   // or after and determines video/audio context
287 
288   pre = (ptr->tag & TC_PREVIEW)? 1:0;
289   vid = (ptr->tag & TC_VIDEO)? 1:0;
290 
291   if( (ptr->tag & TC_PRE_M_PROCESS) && vid && cache_enabled) {
292       process_ctr_cur = (process_ctr_cur+1)%3;
293       ac_memcpy (process_buffer[process_ctr_cur], ptr->video_buf, ptr->video_size);
294       return 0;
295   }
296   if(pre && vid) {
297 
298     if(preview_skip && (ptr->id % preview_skip_num)) return(0);
299 
300     if(!xv_player->display->dontdraw) {
301 
302       //0.6.2 (secondaray buffer for pass-through mode)
303       if (use_secondary_buffer) {
304           ac_memcpy(xv_player->display->pixels[0], ptr->video_buf2, size);
305       } else if (srcfmt != IMG_NONE && destfmt != IMG_NONE) {
306 	  tcv_convert(tcvhandle, ptr->video_buf,
307 		      (uint8_t *)xv_player->display->pixels,
308 		      w, h, srcfmt, destfmt);
309       } else {
310           ac_memcpy(xv_player->display->pixels[0], ptr->video_buf, size);
311       }
312 
313       //display video frame
314       xv_display_show(xv_player->display);
315 
316       if(cache_enabled) preview_cache_submit(xv_player->display->pixels[0], ptr->id, ptr->attributes);
317 
318       if(preview_delay) usleep(preview_delay);
319 
320     } else {
321 
322       //check only for X11-events
323       xv_display_event(xv_player->display);
324 
325     }//dontdraw=1?
326 
327   }//correct slot?
328 
329   return(0);
330 }
331 
preview_cache_init(void)332 int preview_cache_init(void) {
333 
334   //size must be know!
335 
336   int n;
337 
338   if((vid_buf_mem = (char *) calloc(cache_num, size))==NULL) {
339     tc_log_perror(MOD_NAME, "out of memory");
340     return(-1);
341   }
342 
343   if((vid_buf = (char **) calloc(cache_num, sizeof(char *)))==NULL) {
344     tc_log_perror(MOD_NAME, "out of memory");
345     return(-1);
346   }
347 
348   for (n=0; n<cache_num; ++n) vid_buf[n] = (char *) (vid_buf_mem + n * size);
349 
350   cache_enabled=1;
351 
352   return(0);
353 
354 }
355 
preview_cache_submit(char * buf,int id,int flag)356 void preview_cache_submit(char *buf, int id, int flag) {
357 
358   char string[255];
359   memset (string, 0, 255);
360 
361   if(!cache_enabled) return;
362 
363   cache_ptr = (cache_ptr+1)%cache_num;
364 
365   ac_memcpy((char*) vid_buf[cache_ptr], buf, size);
366 
367   (flag & TC_FRAME_IS_KEYFRAME) ? tc_snprintf(string, sizeof(string), "%u *", id) : tc_snprintf(string, sizeof(string), "%u", id);
368 
369   str2img (vid_buf[cache_ptr], string, w, h, cols, rows, 0, 0, CODEC_YUV);
370 }
371 
preview_filter_buffer(int frames_needed)372 int preview_filter_buffer(int frames_needed)
373 {
374     int current,i;
375 
376     static int this_filter = 0;
377     static vframe_list_t *ptr = NULL;
378     vob_t *vob = tc_get_vob();
379 
380     if (ptr == NULL)
381 	ptr = malloc (sizeof (vframe_list_t));
382     memset (ptr, 0, sizeof (vframe_list_t));
383 
384     if (!cache_enabled) return 0;
385 
386     if (this_filter == 0)
387 	this_filter = tc_filter_find("pv");
388 
389     for (current = frames_needed, i = 1; current > 0; current--, i++){
390 
391 #undef NO_PROCESS
392 #ifdef NO_PROCESS
393 	ac_memcpy (run_buffer[0], (char *)vid_buf[cache_ptr-(current-1)], size);
394 	ac_memcpy (run_buffer[1], (char *)vid_buf[cache_ptr-(current-1)], size);
395 #else
396 	ac_memcpy (run_buffer[0], process_buffer[(process_ctr_cur+1)%3], SIZE_RGB_FRAME);
397 	ac_memcpy (run_buffer[1], process_buffer[(process_ctr_cur+1)%3], SIZE_RGB_FRAME);
398 #endif
399 
400 	if (i == 1) {
401 	    ac_memcpy (undo_buffer, (char *)vid_buf[cache_ptr], size);
402 	}
403 
404 	ptr->bufid = 1;
405 	ptr->next = ptr;
406 
407 	ptr->filter_id = 0;
408 	ptr->v_codec = CODEC_YUV;
409 	ptr->id  = i; // frame
410 #ifdef STATBUFFER
411 	ptr->internal_video_buf_0 = run_buffer[0];
412 	ptr->internal_video_buf_1 = run_buffer[1];
413 #endif /* STATBUFFER */
414 
415 	// RGB
416 	ptr->video_buf_RGB[0]=run_buffer[0];
417 	ptr->video_buf_RGB[1]=run_buffer[1];
418 
419 	//YUV
420 	ptr->video_buf_Y[0] = run_buffer[0];
421 	ptr->video_buf_Y[1] = run_buffer[1];
422 
423 	ptr->video_buf_U[0] = ptr->video_buf_Y[0]
424 	    + TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT;
425 	ptr->video_buf_U[1] = ptr->video_buf_Y[1]
426 	    + TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT;
427 
428 	ptr->video_buf_V[0] = ptr->video_buf_U[0]
429 	    + (TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT)/4;
430 	ptr->video_buf_V[1] = ptr->video_buf_U[1]
431 	    + (TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT)/4;
432 
433 	//default pointer
434 	ptr->video_buf  = run_buffer[0];
435 	ptr->video_buf2 = run_buffer[1];
436 	ptr->free = 1;
437 
438 #ifdef NO_PROCESS
439 	ptr->video_size = size;
440 	ptr->v_width = w;
441 	ptr->v_height = h;
442 #else
443 	ptr->v_width = vob->im_v_width;
444 	ptr->v_height = vob->im_v_height;
445 	ptr->video_size = vob->im_v_width*vob->im_v_height*3/2;
446 #endif
447 
448 	// we disable this filter (filter_pv), because it does not make sense
449 	// to be run in the preview loop
450 	tc_filter_disable(this_filter);
451 
452 	// PRE
453 	ptr->tag= TC_VIDEO | TC_PRE_S_PROCESS  | TC_PRE_M_PROCESS;
454 	tc_filter_process((frame_list_t *)ptr);
455 
456 	// CORE
457 	process_vid_frame(vob, ptr);
458 
459 	// POST
460 	ptr->tag= TC_VIDEO | TC_POST_S_PROCESS | TC_POST_M_PROCESS;
461 	tc_filter_process((frame_list_t *)ptr);
462 
463 	tc_filter_enable(this_filter);
464 
465 	ac_memcpy (vid_buf[cache_ptr-current+1], ptr->video_buf, size);
466 	preview_cache_draw(0);
467 
468 	ac_memcpy ((char *)vid_buf[cache_ptr], undo_buffer, size);
469     }
470     return 0;
471 }
472 #if 0
473 void preview_filter(void)
474 {
475     FILE *f;
476     FILE *g;
477     char tmpfile[] = "/tmp/filter-select";
478     char infile[] = "/tmp/filter-in";
479     char buf[1024];
480     char filter_name[255];
481     char *config, *c;
482     int filter_handle, this_filter=-1, disable = 0;
483     int frames_needed = 1;
484     int current=0;
485     int i;
486 
487     if (!cache_enabled) return;
488 
489     // build commandline
490     tc_snprintf (buf, 1024,
491 	   "xterm -title \"Transcode Filter select\" -e %s/filter_list.awk %s %s &&  cat %s && rm -f %s",
492 	   vob->mod_path, vob->mod_path, tmpfile, tmpfile, tmpfile);
493     if ((f = popen (buf, "r")) == NULL) {
494 	perror ("popen filter select");
495 	return;
496     }
497     // recycle
498     memset (buf, 0, 1024);
499 
500     // block until data is available
501     // filter Name
502     fgets(buf, 1024, f);
503     // delete newline
504     buf[strlen(buf)-1] = '\0';
505     strlcpy (filter_name, buf, sizeof(filter_name));
506 
507     // (c)onfig or (d)isable
508     memset (buf, 0, 1024);
509     fgets(buf, 1024, f);
510     buf[strlen(buf)-1] = '\0';
511     if ( strcmp (buf, "(d)") == 0) { disable = 1; }
512 
513     pclose (f);
514 
515     if (disable) {
516 	filter_handle = tc_filter_find(filter_name);
517 	if (filter_handle == -1) {
518 	    // not loaded
519 	    return;
520 	} else {
521 	    tc_filter_disable(filter_handle);
522 	    goto redisplay_frame;
523 	}
524     }
525     filter_handle = tc_filter_add(filter_name, NULL);
526 
527     this_filter  = tc_filter_find("pv");
528     tc_log_msg(MOD_NAME, "this_filter (%d)", this_filter);
529 
530     // we now have a valid ID
531     if ( (config = tc_filter_get_conf(filter_handle, NULL)) == NULL) {
532 	tc_log_warn(MOD_NAME, "Filter \"%s\" can not be configured.", filter_name);
533     }
534 
535     if ((g = fopen(tmpfile, "w")) != NULL) {
536 	fputs (config, g);
537 	fclose (g);
538     } else {
539 	tc_log_warn(MOD_NAME, "unable to write to %s.\n", tmpfile);
540 	return;
541     }
542 
543     if ((c = strchr (config, '\n'))) {
544 	*c = '\0';
545 	optstr_frames_needed (config, &frames_needed);
546     } else
547 	frames_needed = 1;
548 
549     tc_log_msg(MOD_NAME, "XXX optstr_frames_needed:(%d)", frames_needed);
550 
551 
552     free (config);
553 
554     // recycle
555     memset (buf, 0, 1024);
556 
557     tc_snprintf (buf, 1024,
558 	  "xterm -title \"Transcode parameters\" -e %s/parse_csv.awk %s %s %s && cat %s && rm -f %s %s",
559 	  vob->mod_path, tmpfile, filter_name, infile, infile, tmpfile, infile);
560 
561     if ((f = popen (buf, "r")) == NULL) {
562 	perror ("popen filter param");
563 	return;
564     }
565 
566     // recycle
567     memset (buf, 0, 1024);
568 
569     // block until data is available
570     fgets(buf, 1024, f);
571     pclose (f);
572 
573 
574     buf[strlen(buf)-1] = '\0';
575 
576     //tc_log_msg(MOD_NAME, "XX buf (%s)", buf);
577     // XXX
578     if (buf && *buf) {
579 	char *s = strchr(buf, '=');
580 	tc_filter_configure(filter_handle, s ? s+1 : NULL);
581     }
582 
583 redisplay_frame:
584     // logoaway pos=210x136:size=257x175:mode=2
585     for (current = frames_needed, i = 1; current > 0; current--, i++){
586 
587 	vframe_list_t ptr;
588 
589 	if (!disable)
590 	    ac_memcpy (undo_buffer, (char *)vid_buf[cache_ptr], size);
591 
592 	ac_memcpy (run_buffer[0], (char *)vid_buf[cache_ptr-(current-1)], size);
593 	ac_memcpy (run_buffer[1], (char *)vid_buf[cache_ptr-(current-1)], size);
594 
595 	ptr.bufid = 1;
596 	ptr.next = &ptr;
597 	ptr.tag= TC_VIDEO | TC_POST_M_PROCESS | TC_PRE_M_PROCESS |
598 	    TC_POST_S_PROCESS | TC_PRE_S_PROCESS;
599 
600 	ptr.filter_id = 0;
601 	ptr.v_codec = CODEC_YUV;
602 	ptr.id  = i; // frame
603 #ifdef STATBUFFER
604 	ptr.internal_video_buf_0 = run_buffer[0];
605 	ptr.internal_video_buf_1 = run_buffer[1];
606 #endif /* STATBUFFER */
607 
608 	// RGB
609 	ptr.video_buf_RGB[0]=run_buffer[0];
610 	ptr.video_buf_RGB[1]=run_buffer[1];
611 
612 	//YUV
613 	ptr.video_buf_Y[0] = run_buffer[0];
614 	ptr.video_buf_Y[1] = run_buffer[1];
615 
616 	ptr.video_buf_U[0] = ptr.video_buf_Y[0]
617 	    + TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT;
618 	ptr.video_buf_U[1] = ptr.video_buf_Y[1]
619 	    + TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT;
620 
621 	ptr.video_buf_V[0] = ptr.video_buf_U[0]
622 	    + (TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT)/4;
623 	ptr.video_buf_V[1] = ptr.video_buf_U[1]
624 	    + (TC_MAX_V_FRAME_WIDTH * TC_MAX_V_FRAME_HEIGHT)/4;
625 
626 	//default pointer
627 	ptr.video_buf  = run_buffer[0];
628 	ptr.video_buf2 = run_buffer[1];
629 	ptr.free = 1;
630 
631 	ptr.video_size = size;
632 	ptr.v_width = w;
633 	ptr.v_height = h;
634 
635 
636 	// we disable this filter (filter_pv), because it does not make sense
637 	// to be run in the preview loop
638 	tc_filter_disable(this_filter);
639 	tc_filter_process((frame_list_t *)&ptr);
640 	tc_filter_enable(this_filter);
641 
642 	ac_memcpy (vid_buf[cache_ptr-current+1], ptr.video_buf, size);
643 	preview_cache_draw(0);
644     }
645     return;
646 
647 }
648 #endif
649 
preview_cache_undo(void)650 void preview_cache_undo(void)
651 {
652     if (!cache_enabled) return;
653 
654     ac_memcpy((char *)vid_buf[cache_ptr], undo_buffer, size);
655     preview_cache_draw(0);
656 }
657 
preview_cache_draw(int next)658 void preview_cache_draw(int next) {
659 
660   if(!cache_enabled) return;
661 
662   cache_ptr+=next;
663 
664   if(next < 0) cache_ptr+=cache_num;
665   while (cache_ptr<0)
666       cache_ptr+=cache_num;
667 
668   cache_ptr%=cache_num;
669 
670   ac_memcpy(xv_player->display->pixels[0], (char*) vid_buf[cache_ptr], size);
671 
672   //display video frame
673   xv_display_show(xv_player->display);
674 
675   return;
676 
677 }
678 
str2img(char * img,char * c,int width,int height,int char_width,int char_height,int posx,int posy,int codec)679 void str2img(char *img, char *c, int width, int height, int char_width, int char_height, int posx, int posy, int codec)
680 {
681     char **cur;
682     int posxorig=posx;
683 
684     while (*c != '\0' && *c && c) {
685 	if (*c == '\n') {
686 	    posy+=char_height;
687 	    posx=posxorig;
688 	}
689 	if (posx+char_width >= width || posy >= height)
690 	    break;
691 
692 	cur = char2bmp(*c);
693 	if (cur) {
694 	    bmp2img (img, cur, width, height, char_width, char_height, posx, posy, codec);
695 	    posx+=char_width;
696 	}
697 
698 	c++;
699     }
700 }
701 
bmp2img(char * img,char ** c,int width,int height,int char_width,int char_height,int posx,int posy,int codec)702 void bmp2img(char *img, char **c, int width, int height, int char_width, int char_height, int posx, int posy, int codec)
703 {
704     int h, w;
705 
706     if (codec == CODEC_YUV) {
707 	for (h=0; h<char_height; h++) {
708 	    for (w=0; w<char_width; w++) {
709 		img[(posy+h)*width+posx+w] = (c[h][w] == '+')?230:img[(posy+h)*width+posx+w];
710 	    }
711 
712 	}
713     } else {
714 	for (h=0; h<char_height; h++) {
715 	    for (w=0; w<char_width; w++) {
716 		char *col=&img[3*((height-(posy+h))*width+posx+w)];
717 		*(col-0) = (c[h][w] == '+')?255:*(col-0);
718 		*(col-1) = (c[h][w] == '+')?255:*(col-1);
719 		*(col-2) = (c[h][w] == '+')?255:*(col-2);
720 	    }
721 	}
722     }
723     /*
724 	for (h=char_height-1; h>=0; h--) {
725 	    for (w=char_width-1; w>=0; w--) {
726 	    */
727 }
728 
char2bmp(char c)729 char **char2bmp(char c) {
730     switch (c) {
731 	case '0': return  &null_xpm[4];
732 	case '1': return  &one_xpm[4];
733 	case '2': return  &two_xpm[4];
734 	case '3': return  &three_xpm[4];
735 	case '4': return  &four_xpm[4];
736 	case '5': return  &five_xpm[4];
737 	case '6': return  &six_xpm[4];
738 	case '7': return  &seven_xpm[4];
739 	case '8': return  &eight_xpm[4];
740 	case '9': return  &nine_xpm[4];
741 	case '-': return  &minus_xpm[4];
742 	case ':': return  &colon_xpm[4];
743 	case ' ': return  &space_xpm[4];
744 	case '!': return  &exklam_xpm[4];
745 	case '?': return  &quest_xpm[4];
746 	case '.': return  &dot_xpm[4];
747 	case ',': return  &comma_xpm[4];
748 	case ';': return  &semicomma_xpm[4];
749 	case 'A': return  &A_xpm[4];
750 	case 'a': return  &a_xpm[4];
751 	case 'B': return  &B_xpm[4];
752 	case 'b': return  &b_xpm[4];
753 	case 'C': return  &C_xpm[4];
754 	case 'c': return  &c_xpm[4];
755 	case 'D': return  &D_xpm[4];
756 	case 'd': return  &d_xpm[4];
757 	case 'E': return  &E_xpm[4];
758 	case 'e': return  &e_xpm[4];
759 	case 'F': return  &F_xpm[4];
760 	case 'f': return  &f_xpm[4];
761 	case 'G': return  &G_xpm[4];
762 	case 'g': return  &g_xpm[4];
763 	case 'H': return  &H_xpm[4];
764 	case 'h': return  &h_xpm[4];
765 	case 'I': return  &I_xpm[4];
766 	case 'i': return  &i_xpm[4];
767 	case 'J': return  &J_xpm[4];
768 	case 'j': return  &j_xpm[4];
769 	case 'K': return  &K_xpm[4];
770 	case 'k': return  &k_xpm[4];
771 	case 'L': return  &L_xpm[4];
772 	case 'l': return  &l_xpm[4];
773 	case 'M': return  &M_xpm[4];
774 	case 'm': return  &m_xpm[4];
775 	case 'N': return  &N_xpm[4];
776 	case 'n': return  &n_xpm[4];
777 	case 'O': return  &O_xpm[4];
778 	case 'o': return  &o_xpm[4];
779 	case 'P': return  &P_xpm[4];
780 	case 'p': return  &p_xpm[4];
781 	case 'Q': return  &Q_xpm[4];
782 	case 'q': return  &q_xpm[4];
783 	case 'R': return  &R_xpm[4];
784 	case 'r': return  &r_xpm[4];
785 	case 'S': return  &S_xpm[4];
786 	case 's': return  &s_xpm[4];
787 	case 'T': return  &T_xpm[4];
788 	case 't': return  &t_xpm[4];
789 	case 'U': return  &U_xpm[4];
790 	case 'u': return  &u_xpm[4];
791 	case 'V': return  &V_xpm[4];
792 	case 'v': return  &v_xpm[4];
793 	case 'W': return  &W_xpm[4];
794 	case 'w': return  &w_xpm[4];
795 	case 'X': return  &X_xpm[4];
796 	case 'x': return  &x_xpm[4];
797 	case 'Y': return  &Y_xpm[4];
798 	case 'y': return  &y_xpm[4];
799 	case 'Z': return  &Z_xpm[4];
800 	case 'z': return  &z_xpm[4];
801 	case '*': return  &ast_xpm[4];
802 	default: return NULL;
803     }
804     return NULL;
805 }
806 
preview_grab_jpeg(void)807 int preview_grab_jpeg(void)
808 {
809     const char *error;
810     char *prefix = "preview_grab-";
811     vob_t *vob = tc_get_vob();
812     static vob_t *mvob;
813     static void *jpeg_vhandle = NULL;
814     static int (*JPEG_export)(int opt, void *para1, void *para2);
815     static int counter = 0;
816 
817     char module[TC_BUF_MAX];
818     transfer_t export_para;
819     int ret = 0;
820 
821     if(!cache_enabled) return 1;
822 
823     if (jpeg_vhandle == NULL) {
824 	tc_snprintf(module, sizeof(module), "%s/export_%s.so", MOD_PATH, "jpg");
825 	jpeg_vhandle = dlopen(module, RTLD_GLOBAL| RTLD_LAZY);
826 	if (!jpeg_vhandle) {
827 	    tc_log_error(MOD_NAME, "%s", dlerror());
828 	    tc_log_error(MOD_NAME, "loading \"%s\" failed", module);
829 	    return(1);
830 	}
831 	JPEG_export = dlsym(jpeg_vhandle, "tc_export");
832 	if ((error = dlerror()) != NULL)  {
833 	    tc_log_error(MOD_NAME, "%s", error);
834 	    return(1);
835 	}
836 	export_para.flag = TC_DEBUG;
837 	ret = JPEG_export(TC_EXPORT_NAME, &export_para, NULL);
838 
839 	mvob = malloc(sizeof(vob_t));
840 	ac_memcpy (mvob, vob, sizeof(vob_t));
841 	mvob->video_out_file = prefix;
842 
843 	export_para.flag = TC_VIDEO;
844 	if((ret=JPEG_export(TC_EXPORT_INIT, &export_para, mvob))==TC_EXPORT_ERROR) {
845 	    tc_log_error(MOD_NAME, "video jpg export module error: init failed");
846 	    return(1);
847 	}
848 
849 	export_para.flag = TC_VIDEO;
850 	if((ret=JPEG_export(TC_EXPORT_OPEN, &export_para, mvob))==TC_EXPORT_ERROR) {
851 	    tc_log_error(MOD_NAME, "video export module error: open failed");
852 	    return(1);
853 	}
854     }
855 
856     // encode and export video frame
857     export_para.buffer = (char *)vid_buf[cache_ptr];
858     export_para.size   = size;
859     export_para.attributes = TC_FRAME_IS_KEYFRAME;
860     export_para.flag   = TC_VIDEO;
861 
862     if(JPEG_export(TC_EXPORT_ENCODE, &export_para, mvob)<0) {
863 	tc_log_warn(MOD_NAME, "error encoding jpg frame");
864 	return 1;
865     }
866     tc_log_info(MOD_NAME, "Saved JPEG to %s%06d.jpg", prefix, counter++);
867 
868 
869     return 0;
870 }
871 
872 /* vim:sw=4
873  */
874