]> git.vomp.tv Git - vompclient-marten.git/blob - wjpeg.cc
Live radio prebuffering display - code upgrades for connection lost handling
[vompclient-marten.git] / wjpeg.cc
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20 #include "boxx.h"
21 #include "wjpeg.h"
22 #include <setjmp.h>
23
24 #include "i18n.h"
25 #include "log.h"
26 #include "surface.h"
27
28
29 extern "C" void
30 jpeg_memio_src (j_decompress_ptr cinfo, void * userdata);
31 extern "C" void
32 jpeg_memio_cleanup (j_decompress_ptr cinfo);
33
34
35 #ifdef WIN32
36 ULONG WindowsResourceJpegReader::initRead(const char *filename)
37 {
38     HRSRC res=FindResource(NULL,filename,RT_RCDATA);
39     hres=LoadResource(NULL,res);
40     buffer=LockResource(hres);
41     size=SizeofResource(NULL,res);
42     //CloseHandle(hres);
43     return size;
44 }
45
46  ULONG WindowsResourceJpegReader::readChunk(ULONG offset,ULONG len,char **buf)
47 {
48     if (offset>size) return 0;
49     ULONG toread=min(size-offset,len);
50     char* buffy=(char*)malloc(toread);
51     memcpy(buffy,((char*)buffer)+offset,toread);
52     *buf=buffy;
53     return toread;
54 }
55
56  WindowsResourceJpegReader::~WindowsResourceJpegReader(){
57     buffer=NULL;
58     size=0;
59     FreeResource(hres);
60
61 }
62 #endif
63
64 WJpeg::WJpeg(){
65   fileName=NULL;
66 #ifndef WIN32
67   reader=NULL;
68 #else
69   reader=&winread;
70 #endif
71   useImageDimensions=true;
72   jheight=0;
73   jwidth=0;
74   jsize=0;
75   jerror=true;
76   rotate=0;
77   jscale=1;
78 }
79
80 WJpeg::~WJpeg() {
81   if (fileName) delete fileName;
82 }
83
84 int WJpeg::init(char* tfileName,bool useImage, JpegReader *rdr)
85 {
86   rotate=0;
87   if (fileName) delete fileName;
88   fileName=NULL;
89   if (tfileName) {
90     fileName = new char[strlen(tfileName)+1];
91     strcpy(fileName,tfileName);
92   }
93   if (rdr!=NULL) {
94   reader=rdr;
95   } else {
96 #ifdef WIN32
97       reader=&winread;
98 #else
99       reader=NULL;
100 #endif
101   }
102   useImageDimensions=useImage;
103   return 1;
104 }
105
106
107 ULONG WJpeg::getJpegInfo(ULONG tag){
108   switch(tag) {
109   case JPEG_HEIGHT:
110      return jheight;
111      break;
112   case JPEG_WIDTH:
113      return jwidth;
114      break;
115   case JPEG_SIZE:
116      return jsize;
117      break;
118   case JPEG_ROTATE:
119      return rotate;
120      break;
121   case JPEG_SCALE:
122      return jscale;
123      break;
124   }
125   return 0;
126 }
127
128 int WJpeg::getJpegInfo(ULONG tag, char * buffer) {
129   switch (tag) {
130   case JPEG_FILENAME:
131     strncpy(buffer,fileName,INFO_BUFFER-1);
132     buffer[INFO_BUFFER-1]=0;
133     return 0;
134     break;
135   }
136   return -1;
137 }
138
139 void WJpeg::setRotate(int amount) {
140   rotate=amount;
141 }
142
143
144
145 extern "C" {
146
147
148 struct my_error_mgr {
149   struct jpeg_error_mgr pub;    /* "public" fields */
150   FILE *infile;                 /* to be used in error handler */
151
152   jmp_buf setjmp_buffer;        /* for return to caller */
153 };
154
155 typedef struct my_error_mgr * my_error_ptr;
156
157 /*
158  * Here's the routine that will replace the standard error_exit method:
159  */
160
161 METHODDEF(void)
162 my_error_exit (j_common_ptr cinfo)
163 {
164   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
165   my_error_ptr myerr = (my_error_ptr) cinfo->err;
166
167   /* Always display the message. */
168   /* We could postpone this until after returning, if we chose. */
169   (*cinfo->err->output_message) (cinfo);
170   if (myerr->infile) fclose(myerr->infile);
171   /* Return control to the setjmp point */
172   longjmp(myerr->setjmp_buffer, 1);
173 }
174
175 }
176
177 bool WJpeg::hasError() {
178   return jerror;
179 }
180 void WJpeg::draw()
181 {
182   jerror=false;
183   if (drawJpeg() != 0) {
184     jerror=true;
185     drawTextCentre(tr("Jpeg ERROR"), 240, 180, Colour::LIGHTTEXT);
186   }
187 }
188
189 /* handle picture rotation
190    90: xr=h-y
191        yr=x
192    180:xr=w-x
193        yr=h-y
194    270:xr=y
195        yr=w-x
196 */
197 void WJpeg::drawPixel(int x, int y,int w,int h,int xpos, int ypos, const Colour& c){
198   int xb=0;
199   int yb=0;
200   switch(rotate) {
201      case ROT_0:
202         xb=x;
203         yb=y;
204         break;
205      case ROT_90:
206         xb=h-y;
207         yb=x;
208         break;
209      case ROT_180:
210         xb=w-x;
211         yb=h-y;
212         break;
213      case ROT_270:
214         xb=y;
215         yb=w-x;
216         break;
217      }
218    xb+=xpos;
219    yb+=ypos;
220    if (xb < 0 || yb < 0 ) {
221      Log::getInstance()->log("WJpeg:drawPixel",Log::ERR,"pixpos < 0 x=%d, y=%d",xb,yb);
222      return;
223    }
224    Boxx::drawPixel((UINT)xb,(UINT)yb,c);
225 }
226
227 int WJpeg::drawJpeg() {
228 //#ifndef WIN32
229   Log* logger = Log::getInstance();
230   unsigned char* buffer =NULL;
231   struct jpeg_decompress_struct cinfo;
232   struct my_error_mgr jerr;
233   jerr.infile=NULL;
234   cinfo.err = jpeg_std_error(&jerr.pub);
235   jerr.pub.error_exit = my_error_exit;
236   /* Establish the setjmp return context for my_error_exit to use. */
237   if (setjmp(jerr.setjmp_buffer)) {
238     /* If we get here, the JPEG code has signaled an error.
239      * We need to clean up the JPEG object, close the input file, and return.
240      */
241     if (reader) jpeg_memio_cleanup(&cinfo);
242     jpeg_destroy_decompress(&cinfo);
243     logger->log("BJpeg", Log::ERR, "JPEG error");
244     if (buffer) free(buffer);
245     return 1;
246   }
247   int xpos=0;
248   int ypos=0;
249   jpeg_create_decompress(&cinfo);
250   if (fileName && ! reader) {
251     jsize=0; //TODO: compute size for local files
252 #ifdef WIN32
253     jerr.infile=fopen(fileName+1, "rb");
254 #else 
255     jerr.infile=fopen(fileName, "rb");
256 #endif
257     if (jerr.infile == NULL)
258     {
259       logger->log("BJpeg", Log::ERR, "Can't open JPEG");
260       jpeg_destroy_decompress(&cinfo);
261       return 1;
262     }
263     logger->log("BJpeg", Log::DEBUG, "File opened");
264     jpeg_stdio_src(&cinfo, jerr.infile);
265   }
266   else if (reader) {
267      jsize=reader->initRead(fileName);
268      if (jsize <= 0) {
269         logger->log("BJpeg", Log::ERR, "Can't init JPEG transfer");
270         jpeg_destroy_decompress(&cinfo);
271         return 1;
272      }
273      jpeg_memio_src(&cinfo,(void *)reader);
274   }
275   else {
276      logger->log("BJpeg", Log::ERR, "neither  filename nor reader set");
277      jpeg_destroy_decompress(&cinfo);
278      return 1;
279   }
280   jpeg_read_header(&cinfo, TRUE);
281   logger->log("BJpeg", Log::DEBUG, "JPEG read header w=%i h=%i, rot=%i", cinfo.image_width, cinfo.image_height, rotate);
282   int picturew=cinfo.image_width;
283   int pictureh=cinfo.image_height;
284   switch (rotate){
285     case ROT_90:
286     case ROT_270:
287       pictureh=cinfo.image_width;
288       picturew=cinfo.image_height;
289       break;
290   }
291   if (! useImageDimensions) {
292     //scale - we can have factors 1,2,4,8
293     int scalew=getWidth()*1000/picturew;
294     int scaleh=getHeight()*1000/pictureh;
295     int scale=scaleh;
296     if (scalew < scaleh) scale=scalew;
297     int fac=8;
298     //we allow for 10% bigger...
299     if (scale >= 900) fac=1;
300     else if (scale >= 450 ) fac=2;
301     else if (scale >= 225 ) fac=4;
302     cinfo.scale_denom=fac;
303     logger->log("BJpeg", Log::DEBUG, "JPEG scaling (1/1000) pw=%i ph=%i w=%i h=%i r=%i f=%i",
304        getWidth(),
305        getHeight(),
306        scalew,scaleh,scale,fac);
307     jscale=fac;
308     }
309
310   //remember picture parameters
311   jheight=pictureh;
312   jwidth=picturew;
313
314   jpeg_start_decompress(&cinfo);
315   //recompute width based on rotation (consider scale)
316   picturew=cinfo.output_width;
317   pictureh=cinfo.output_height;
318   switch (rotate){
319     case ROT_90:
320     case ROT_270:
321       pictureh=cinfo.output_width;
322       picturew=cinfo.output_height;
323       break;
324   }  
325   //if our image is smaller - center it
326   if (! useImageDimensions) {
327      xpos=(((int)getWidth())-picturew)/2;
328      if (xpos <0) xpos=0;
329      ypos=(((int)getHeight())-pictureh)/2;
330      if (ypos <0) ypos=0;
331   }
332   //center point for rotation
333   int w=cinfo.output_width;
334   int h=cinfo.output_height;
335   logger->log("BJpeg", Log::DEBUG, "JPEG startup done pw=%i ph=%i, xo=%i,yo=%i, iw=%i, ih=%i", picturew, pictureh,xpos,ypos,w,h);
336
337
338   if (useImageDimensions) setDimensions(picturew, pictureh);
339   fillColour(backgroundColour);
340
341
342   //line len in bytes (3 bytes per Pixel) - for buffer (can be bigger then surface)
343   int linelen=cinfo.output_width*3;
344 #ifdef USE_BUFFER
345   // MAKE THE 2D ARRAY
346   buffer = (unsigned char*)malloc(config.output_height * linelen);
347   logger->log("BJpeg", Log::DEBUG, "Buffer allocated at %p, width = %i height = %i", buffer, cinfo.output_height, linelen);
348   if (buffer == NULL) {
349     if (reader) jpeg_memio_cleanup(&cinfo);
350     jpeg_destroy_decompress(&cinfo);
351     if (jerr.infile) fclose(jerr.infile);
352     logger->log("BJpeg", Log::ERR, "JPEG error - no room for buffer");
353     return 1;
354   }
355 #endif
356
357   //unsigned char* bufferPointers[area.h];
358   //for(UINT ps = 0; ps < area.h; ps++) bufferPointers[ps] = buffer + (ps * area.w * 3);
359   
360   logger->log("BJpeg", Log::DEBUG, "header w=%d,h=%d",cinfo.output_width,cinfo.output_height);
361 #ifndef USE_BUFFER
362   //unsigned char lbuf[linelen];
363   unsigned char *lbuf=new unsigned char[linelen];
364   unsigned char * ptr=lbuf;
365 #else
366   unsigned char * ptr=buffer;
367 #endif
368
369   int rowsread = 0;
370
371   Colour c;
372   startFastDraw();//Tell the surface, that we will draw a lot of pixel,
373   //so that performance, can be optimized
374   while (cinfo.output_scanline < cinfo.output_height)
375   {
376 //  logger->log("BJpeg", Log::DEBUG, "%i", rowsread);
377     rowsread += jpeg_read_scanlines(&cinfo,&ptr,1);
378 #ifdef USE_BUFFER
379     ptr+=linelen;
380 #else
381      int x=0;
382      for (unsigned char * lp=ptr;lp < (ptr+linelen);lp+=3,x++) {
383        c.red=*lp ;
384        c.green=*(lp+1);
385        c.blue=*(lp+2);
386        drawPixel(x, rowsread-1,w,h,xpos,ypos, c);
387        }
388     
389 #endif
390   }
391   endFastDraw();
392
393   logger->log("BJpeg", Log::DEBUG, "Done all jpeg_read");
394
395   jpeg_finish_decompress(&cinfo);
396   if (reader) jpeg_memio_cleanup(&cinfo);
397   jpeg_destroy_decompress(&cinfo);
398
399   if (jerr.infile) fclose(jerr.infile);
400
401   logger->log("BJpeg", Log::DEBUG, "jpeg shutdown done, x, y %u %u", area.w, area.h);
402 #ifdef USE_BUFFER
403   Colour c;
404   UINT x, y, xoff;
405   unsigned char* p;
406   surface->startFastDraw();//Tell the surface, that we will draw a lot of pixel,
407   //so that performance, can be optimized
408   for (y = 0; y < numlines; y++)
409   {
410     p = bufferPointers[y];
411
412     for (x = 0; x < sfclen; x++)
413     {
414       xoff = x * 3;
415
416 //      c = (  (0xFF000000        )
417 //           | (p[xoff    ]  << 16)
418 //           | (p[xoff + 1]  <<  8)
419 //           | (p[xoff + 2]       ) );
420
421       c.red = p[xoff];
422       c.green = p[xoff + 1];
423       c.blue = p[xoff + 2];
424
425       drawPixel(x, y, w,h,xpos,ypos,c);
426     }
427   }
428   surface->endFastDraw();
429   delete [] lbuf;
430   free(buffer);
431 #endif
432   logger->log("BJpeg", Log::DEBUG, "deleted buffer");
433 /*#else
434   DWORD width,height;
435   width=height=1;
436   if (!reader) 
437   {
438   ((SurfaceWin*)surface)->drawJpeg(fileName+1,offsetX,offsetY,&width,&height);//This should went into the abstract base classes?
439   //Windows has a problem with the leading / fixme
440   } else {
441       ULONG jsize;
442       jsize=reader->initRead(fileName);
443       char *buffer;
444       if (jsize==reader->readChunk(0,jsize,&buffer))
445       {
446            ((SurfaceWin*)surface)->drawJpeg(buffer,jsize,offsetX,offsetY,&width,&height);//This should went into the abstract base classes?
447             free(buffer);
448       }
449
450   }
451   
452  // setDimensions(width, height);
453   jwidth=width;
454   jheight=height;
455
456 #endif*/
457   return 0;
458 }
459
460
461 extern "C" {
462 ULONG jpeg_call_reader(ULONG offset,ULONG size,char ** buf,void *cb) {
463   JpegReader *rd=(JpegReader *)cb;
464   return rd->readChunk(offset,size,buf);
465 }
466 }
467 //the memory buffer reader for the jpeg lib
468 //taken from jdatasrc.c
469
470 extern "C" {
471 /* Expanded data source object for stdio input */
472 #include "jinclude.h"
473 #include "jpeglib.h"
474 #include "jerror.h"/* Expanded data source object for stdio input */
475
476 typedef struct {
477   struct jpeg_source_mgr pub;   /* public fields */
478
479   JOCTET * buffer;              /* start of buffer */
480   boolean start_of_file;        /* have we gotten any data yet? */
481   void * userdata;              /* used for callback */
482   ULONG offset;
483 } my_source_mgr;
484
485 typedef my_source_mgr * my_src_ptr;
486
487 #define INPUT_BUF_SIZE  (64*4096)       /* choose an efficiently fread'able size */
488
489
490 /*
491  * Initialize source --- called by jpeg_read_header
492  * before any data is actually read.
493  */
494
495 METHODDEF(void)
496 linit_source (j_decompress_ptr cinfo)
497 {
498   my_src_ptr src = (my_src_ptr) cinfo->src;
499
500   /* We reset the empty-input-file flag for each image,
501    * but we don't clear the input buffer.
502    * This is correct behavior for reading a series of images from one source.
503    */
504   src->start_of_file = TRUE;
505   src->offset=0;
506 }
507
508
509 /*
510  * Fill the input buffer --- called whenever buffer is emptied.
511  *
512  * In typical applications, this should read fresh data into the buffer
513  * (ignoring the current state of next_input_byte & bytes_in_buffer),
514  * reset the pointer & count to the start of the buffer, and return TRUE
515  * indicating that the buffer has been reloaded.  It is not necessary to
516  * fill the buffer entirely, only to obtain at least one more byte.
517  *
518  * There is no such thing as an EOF return.  If the end of the file has been
519  * reached, the routine has a choice of ERREXIT() or inserting fake data into
520  * the buffer.  In most cases, generating a warning message and inserting a
521  * fake EOI marker is the best course of action --- this will allow the
522  * decompressor to output however much of the image is there.  However,
523  * the resulting error message is misleading if the real problem is an empty
524  * input file, so we handle that case specially.
525  *
526  * In applications that need to be able to suspend compression due to input
527  * not being available yet, a FALSE return indicates that no more data can be
528  * obtained right now, but more may be forthcoming later.  In this situation,
529  * the decompressor will return to its caller (with an indication of the
530  * number of scanlines it has read, if any).  The application should resume
531  * decompression after it has loaded more data into the input buffer.  Note
532  * that there are substantial restrictions on the use of suspension --- see
533  * the documentation.
534  *
535  * When suspending, the decompressor will back up to a convenient restart point
536  * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
537  * indicate where the restart point will be if the current call returns FALSE.
538  * Data beyond this point must be rescanned after resumption, so move it to
539  * the front of the buffer rather than discarding it.
540  */
541
542 METHODDEF(boolean)
543 lfill_input_buffer (j_decompress_ptr cinfo)
544 {
545   my_src_ptr src = (my_src_ptr) cinfo->src;
546   size_t nbytes;
547   if (src->buffer) free(src->buffer);
548   src->buffer=NULL;
549   nbytes = jpeg_call_reader(src->offset, INPUT_BUF_SIZE,(char **)&(src->buffer), src->userdata);
550
551   if (nbytes <= 0) {
552     WARNMS(cinfo, JWRN_JPEG_EOF);
553     src->buffer =  (JOCTET *)malloc(2);
554     src->buffer[0] = (JOCTET) 0xFF;
555     src->buffer[1] = (JOCTET) JPEG_EOI;
556     nbytes = 2;
557
558   }
559   src->offset+=nbytes;
560
561   src->pub.next_input_byte = src->buffer;
562   src->pub.bytes_in_buffer = nbytes;
563   src->start_of_file = FALSE;
564
565   return TRUE;
566 }
567
568
569 /*
570  * Skip data --- used to skip over a potentially large amount of
571  * uninteresting data (such as an APPn marker).
572  *
573  * Writers of suspendable-input applications must note that skip_input_data
574  * is not granted the right to give a suspension return.  If the skip extends
575  * beyond the data currently in the buffer, the buffer can be marked empty so
576  * that the next read will cause a fill_input_buffer call that can suspend.
577  * Arranging for additional bytes to be discarded before reloading the input
578  * buffer is the application writer's problem.
579  */
580
581 METHODDEF(void)
582 lskip_input_data (j_decompress_ptr cinfo, long num_bytes)
583 {
584   my_src_ptr src = (my_src_ptr) cinfo->src;
585
586   /* Just a dumb implementation for now.  Could use fseek() except
587    * it doesn't work on pipes.  Not clear that being smart is worth
588    * any trouble anyway --- large skips are infrequent.
589    */
590   if (num_bytes > 0) {
591     while (num_bytes > (long) src->pub.bytes_in_buffer) {
592       num_bytes -= (long) src->pub.bytes_in_buffer;
593       (void) lfill_input_buffer(cinfo);
594       /* note we assume that fill_input_buffer will never return FALSE,
595        * so suspension need not be handled.
596        */
597     }
598     src->pub.next_input_byte += (size_t) num_bytes;
599     src->pub.bytes_in_buffer -= (size_t) num_bytes;
600   }
601 }
602
603
604 /*
605  * An additional method that can be provided by data source modules is the
606  * resync_to_restart method for error recovery in the presence of RST markers.
607  * For the moment, this source module just uses the default resync method
608  * provided by the JPEG library.  That method assumes that no backtracking
609  * is possible.
610  */
611
612
613 /*
614  * Terminate source --- called by jpeg_finish_decompress
615  * after all data has been read.  Often a no-op.
616  *
617  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
618  * application must deal with any cleanup that should happen even
619  * for error exit.
620  */
621
622 METHODDEF(void)
623 lterm_source (j_decompress_ptr cinfo)
624 {
625   /* no work necessary here */
626 }
627
628
629 /*
630  * Prepare for input from a stdio stream.
631  * The caller must have already opened the stream, and is responsible
632  * for closing it after finishing decompression.
633  */
634
635 extern "C" void
636 jpeg_memio_src (j_decompress_ptr cinfo, void * userdata)
637 {
638   my_src_ptr src;
639
640   /* The source object and input buffer are made permanent so that a series
641    * of JPEG images can be read from the same file by calling jpeg_stdio_src
642    * only before the first one.  (If we discarded the buffer at the end of
643    * one image, we'd likely lose the start of the next one.)
644    * This makes it unsafe to use this manager and a different source
645    * manager serially with the same JPEG object.  Caveat programmer.
646    */
647   if (cinfo->src == NULL) {     /* first time for this JPEG object? */
648     cinfo->src = (struct jpeg_source_mgr *)
649       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
650                                   SIZEOF(my_source_mgr));
651     src = (my_src_ptr) cinfo->src;
652     src->buffer = NULL;
653   }
654
655   src = (my_src_ptr) cinfo->src;
656   src->pub.init_source = linit_source;
657   src->pub.fill_input_buffer = lfill_input_buffer;
658   src->pub.skip_input_data = lskip_input_data;
659   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
660   src->pub.term_source = lterm_source;
661   src->userdata=userdata;
662   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
663   src->pub.next_input_byte = NULL; /* until buffer loaded */
664   src->offset=0;
665   src->userdata=userdata;
666   if (src->buffer) {
667      free(src->buffer);
668      src->buffer=NULL;
669      }
670 }
671 /* cleanup to be called before cleanup of cinfo*/
672 extern "C" void
673 jpeg_memio_cleanup (j_decompress_ptr cinfo) {
674   my_src_ptr src=(my_src_ptr)cinfo->src;
675   Log::getInstance()->log("BJpeg", Log::DEBUG, "cleanup src, src=%p, buf=%p",src,(src?src->buffer:0));
676   if (src && src->buffer) {
677     free(src->buffer);
678     src->buffer=NULL;
679     }
680 }
681 }
682