1 /*
2 (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org)
3 (c) Copyright 2000-2004 Convergence (integrated media) GmbH
4
5 All rights reserved.
6
7 Written by Denis Oliver Kropp <dok@directfb.org>,
8 Andreas Hundt <andi@fischlustig.de>,
9 Sven Neumann <neo@directfb.org>,
10 Ville Syrjälä <syrjala@sci.fi> and
11 Claudio Ciccani <klan@users.sf.net>.
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the
25 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 Boston, MA 02111-1307, USA.
27 */
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/time.h>
36
37 #include <pthread.h>
38
39 #include <directfb.h>
40
41 #include <direct/types.h>
42 #include <direct/mem.h>
43 #include <direct/memcpy.h>
44 #include <direct/messages.h>
45 #include <direct/thread.h>
46 #include <direct/util.h>
47
48 #include <idirectfb.h>
49
50 #include <core/surface.h>
51
52 #include <display/idirectfbsurface.h>
53
54 #include <media/idirectfbdatabuffer.h>
55 #include <media/idirectfbvideoprovider.h>
56
57 #include <misc/gfx_util.h>
58
59
60 static DFBResult Probe( IDirectFBVideoProvider_ProbeContext *ctx );
61
62 static DFBResult Construct( IDirectFBVideoProvider *thiz,
63 IDirectFBDataBuffer *buffer );
64
65
66 #include <direct/interface_implementation.h>
67
68 DIRECT_INTERFACE_IMPLEMENTATION( IDirectFBVideoProvider, Gif )
69
70 /*****************************************************************************/
71
72 #define MAXCOLORMAPSIZE 256
73
74 #define CM_RED 0
75 #define CM_GREEN 1
76 #define CM_BLUE 2
77
78 #define MAX_LWZ_BITS 12
79
80 #define INTERLACE 0x40
81 #define LOCALCOLORMAP 0x80
82
83 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
84
85 #define LM_to_uint(a,b) (((b)<<8)|(a))
86
87 typedef struct {
88 int ref; /* reference counter */
89
90 IDirectFBDataBuffer *buffer;
91 DFBBoolean seekable;
92
93 IDirectFBSurface *destination;
94 IDirectFBSurface_data *dst_data;
95 DFBRectangle dst_rect;
96
97 u32 *image;
98
99 DirectThread *thread;
100 pthread_mutex_t lock;
101 pthread_cond_t cond;
102
103 DFBVideoProviderStatus status;
104 DFBVideoProviderPlaybackFlags flags;
105 double speed;
106
107 unsigned int start_pos;
108
109 char Version[4];
110 unsigned int Width;
111 unsigned int Height;
112 u8 ColorMap[3][MAXCOLORMAPSIZE];
113 unsigned int BitPixel;
114 unsigned int ColorResolution;
115 u32 Background;
116 unsigned int AspectRatio;
117
118 int transparent;
119 unsigned int delayTime;
120 int inputFlag;
121 int disposal;
122
123 u8 buf[280];
124 int curbit, lastbit, done, last_byte;
125
126 int fresh;
127 int code_size, set_code_size;
128 int max_code, max_code_size;
129 int firstcode, oldcode;
130 int clear_code, end_code;
131 int table[2][(1<< MAX_LWZ_BITS)];
132 int stack[(1<<(MAX_LWZ_BITS))*2], *sp;
133
134 DVFrameCallback callback;
135 void *callback_ctx;
136 } IDirectFBVideoProvider_GIF_data;
137
138 #define GIFERRORMSG(x, ...) \
139 D_ERROR( "IDirectFBVideoProvider_GIF: " #x "!\n", ## __VA_ARGS__ )
140
141 #define GIFDEBUGMSG(x, ...) \
142 D_DEBUG( "IDirectFBVideoProvider_GIF: " #x "!\n", ## __VA_ARGS__ )
143
144 /*****************************************************************************/
145
146 static int ZeroDataBlock = 0;
147
148 static DFBResult
FetchData(IDirectFBDataBuffer * buffer,void * data,unsigned int len)149 FetchData( IDirectFBDataBuffer *buffer, void *data, unsigned int len )
150 {
151 DFBResult ret = DFB_OK;
152
153 do {
154 unsigned int read = 0;
155
156 ret = buffer->WaitForData( buffer, len );
157 if (ret == DFB_OK)
158 ret = buffer->GetData( buffer, len, data, &read );
159 if (ret)
160 break;
161
162 data += read;
163 len -= read;
164 } while (len);
165
166 return ret;
167 }
168
ReadColorMap(IDirectFBDataBuffer * buffer,int number,u8 buf[3][MAXCOLORMAPSIZE])169 static int ReadColorMap( IDirectFBDataBuffer *buffer, int number,
170 u8 buf[3][MAXCOLORMAPSIZE] )
171 {
172 int i;
173 u8 rgb[3*number];
174
175 if (FetchData( buffer, rgb, sizeof(rgb) )) {
176 GIFERRORMSG("bad colormap");
177 return -1;
178 }
179
180 for (i = 0; i < number; ++i) {
181 buf[CM_RED][i] = rgb[i*3+0];
182 buf[CM_GREEN][i] = rgb[i*3+1];
183 buf[CM_BLUE][i] = rgb[i*3+2];
184 }
185
186 return 0;
187 }
188
GetDataBlock(IDirectFBDataBuffer * buffer,u8 * buf)189 static int GetDataBlock(IDirectFBDataBuffer *buffer, u8 *buf)
190 {
191 unsigned char count;
192
193 if (FetchData( buffer, &count, 1 )) {
194 GIFERRORMSG("error in getting DataBlock size");
195 return -1;
196 }
197 ZeroDataBlock = (count == 0);
198
199 if ((count != 0) && FetchData( buffer, buf, count )) {
200 GIFERRORMSG("error in reading DataBlock");
201 return -1;
202 }
203
204 return count;
205 }
206
GetCode(IDirectFBVideoProvider_GIF_data * data,int code_size,int flag)207 static int GetCode(IDirectFBVideoProvider_GIF_data *data, int code_size, int flag)
208 {
209 int i, j, ret;
210 unsigned char count;
211
212 if (flag) {
213 data->curbit = 0;
214 data->lastbit = 0;
215 data->done = false;
216 return 0;
217 }
218
219 if ( (data->curbit+code_size) >= data->lastbit) {
220 if (data->done) {
221 if (data->curbit >= data->lastbit) {
222 GIFERRORMSG("ran off the end of my bits");
223 }
224 return -1;
225 }
226 data->buf[0] = data->buf[data->last_byte-2];
227 data->buf[1] = data->buf[data->last_byte-1];
228
229 if ((count = GetDataBlock( data->buffer, &data->buf[2] )) == 0) {
230 data->done = true;
231 }
232
233 data->last_byte = 2 + count;
234 data->curbit = (data->curbit - data->lastbit) + 16;
235 data->lastbit = (2+count) * 8;
236 }
237
238 ret = 0;
239 for (i = data->curbit, j = 0; j < code_size; ++i, ++j) {
240 ret |= ((data->buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
241 }
242 data->curbit += code_size;
243
244 return ret;
245 }
246
DoExtension(IDirectFBVideoProvider_GIF_data * data,int label)247 static int DoExtension( IDirectFBVideoProvider_GIF_data *data, int label )
248 {
249 unsigned char buf[256] = { 0 };
250 char *str;
251
252 switch (label) {
253 case 0x01: /* Plain Text Extension */
254 str = "Plain Text Extension";
255 break;
256 case 0xff: /* Application Extension */
257 str = "Application Extension";
258 break;
259 case 0xfe: /* Comment Extension */
260 str = "Comment Extension";
261 while (GetDataBlock( data->buffer, (u8*) buf ) != 0)
262 GIFDEBUGMSG("gif comment: %s", buf);
263 return false;
264 case 0xf9: /* Graphic Control Extension */
265 str = "Graphic Control Extension";
266 (void) GetDataBlock( data->buffer, (u8*) buf );
267 data->disposal = (buf[0] >> 2) & 0x7;
268 data->inputFlag = (buf[0] >> 1) & 0x1;
269 if (LM_to_uint( buf[1], buf[2] ))
270 data->delayTime = LM_to_uint( buf[1], buf[2] ) * 10000;
271 if ((buf[0] & 0x1) != 0)
272 data->transparent = buf[3];
273 else
274 data->transparent = -1;
275 while (GetDataBlock( data->buffer, (u8*) buf ) != 0)
276 ;
277 return false;
278 default:
279 str = (char*) buf;
280 snprintf(str, 256, "UNKNOWN (0x%02x)", label);
281 break;
282 }
283
284 GIFDEBUGMSG("got a '%s' extension", str );
285
286 while (GetDataBlock( data->buffer, (u8*) buf ) != 0);
287
288 return 0;
289 }
290
LWZReadByte(IDirectFBVideoProvider_GIF_data * data,int flag,int input_code_size)291 static int LWZReadByte( IDirectFBVideoProvider_GIF_data *data, int flag, int input_code_size )
292 {
293 int code, incode;
294 int i;
295
296 if (flag) {
297 data->set_code_size = input_code_size;
298 data->code_size = data->set_code_size+1;
299 data->clear_code = 1 << data->set_code_size ;
300 data->end_code = data->clear_code + 1;
301 data->max_code_size = 2*data->clear_code;
302 data->max_code = data->clear_code+2;
303
304 GetCode(data, 0, true);
305
306 data->fresh = true;
307
308 for (i = 0; i < data->clear_code; ++i) {
309 data->table[0][i] = 0;
310 data->table[1][i] = i;
311 }
312 for (; i < (1<<MAX_LWZ_BITS); ++i) {
313 data->table[0][i] = data->table[1][0] = 0;
314 }
315 data->sp = data->stack;
316
317 return 0;
318 }
319 else if (data->fresh) {
320 data->fresh = false;
321 do {
322 data->firstcode = data->oldcode = GetCode( data, data->code_size, false );
323 } while (data->firstcode == data->clear_code);
324
325 return data->firstcode;
326 }
327
328 if (data->sp > data->stack) {
329 return *--data->sp;
330 }
331
332 while ((code = GetCode( data, data->code_size, false )) >= 0) {
333 if (code == data->clear_code) {
334 for (i = 0; i < data->clear_code; ++i) {
335 data->table[0][i] = 0;
336 data->table[1][i] = i;
337 }
338 for (; i < (1<<MAX_LWZ_BITS); ++i) {
339 data->table[0][i] = data->table[1][i] = 0;
340 }
341 data->code_size = data->set_code_size+1;
342 data->max_code_size = 2*data->clear_code;
343 data->max_code = data->clear_code+2;
344 data->sp = data->stack;
345 data->firstcode = data->oldcode = GetCode( data, data->code_size, false );
346
347 return data->firstcode;
348 }
349 else if (code == data->end_code) {
350 int count;
351 u8 buf[260];
352
353 if (ZeroDataBlock) {
354 return -2;
355 }
356
357 while ((count = GetDataBlock( data->buffer, buf )) > 0)
358 ;
359
360 if (count != 0)
361 GIFERRORMSG("missing EOD in data stream (common occurence)");
362
363 return -2;
364 }
365
366 incode = code;
367
368 if (code >= data->max_code) {
369 *data->sp++ = data->firstcode;
370 code = data->oldcode;
371 }
372
373 while (code >= data->clear_code) {
374 *data->sp++ = data->table[1][code];
375 if (code == data->table[0][code]) {
376 GIFERRORMSG("circular table entry BIG ERROR");
377 }
378 code = data->table[0][code];
379 }
380
381 *data->sp++ = data->firstcode = data->table[1][code];
382
383 if ((code = data->max_code) <(1<<MAX_LWZ_BITS)) {
384 data->table[0][code] = data->oldcode;
385 data->table[1][code] = data->firstcode;
386 ++data->max_code;
387 if ((data->max_code >= data->max_code_size)
388 && (data->max_code_size < (1<<MAX_LWZ_BITS)))
389 {
390 data->max_code_size *= 2;
391 ++data->code_size;
392 }
393 }
394
395 data->oldcode = incode;
396
397 if (data->sp > data->stack) {
398 return *--data->sp;
399 }
400 }
401 return code;
402 }
403
ReadImage(IDirectFBVideoProvider_GIF_data * data,int left,int top,int width,int height,u8 cmap[3][MAXCOLORMAPSIZE],bool interlace,bool ignore)404 static int ReadImage( IDirectFBVideoProvider_GIF_data *data,
405 int left, int top, int width, int height,
406 u8 cmap[3][MAXCOLORMAPSIZE], bool interlace, bool ignore )
407 {
408 u8 c;
409 int v;
410 int xpos = 0, ypos = 0, pass = 0;
411 u32 *image, *dst;
412
413 /*
414 ** Initialize the decompression routines
415 */
416 if (FetchData( data->buffer, &c, 1 ))
417 GIFERRORMSG("EOF / read error on image data");
418
419 if (LWZReadByte( data, true, c ) < 0)
420 GIFERRORMSG("error reading image");
421
422 /*
423 ** If this is an "uninteresting picture" ignore it.
424 */
425 if (ignore) {
426 GIFDEBUGMSG("skipping image...");
427
428 while (LWZReadByte( data, false, c ) >= 0)
429 ;
430 return 0;
431 }
432
433 switch (data->disposal) {
434 case 2:
435 GIFDEBUGMSG("restoring to background color...");
436 memset( data->image, 0, data->Width * data->Height * 4 );
437 break;
438 case 3:
439 GIFERRORMSG("restoring to previous frame is unsupported");
440 break;
441 default:
442 break;
443 }
444
445 dst = image = data->image + (top * data->Width + left);
446
447 GIFDEBUGMSG("reading %dx%d at %dx%d %sGIF image",
448 width, height, left, top, interlace ? " interlaced " : "" );
449
450 while ((v = LWZReadByte( data, false, c )) >= 0 ) {
451 if (v != data->transparent) {
452 dst[xpos] = (0xFF000000 |
453 cmap[CM_RED][v] << 16 |
454 cmap[CM_GREEN][v] << 8 |
455 cmap[CM_BLUE][v]);
456 }
457
458 ++xpos;
459 if (xpos == width) {
460 xpos = 0;
461 if (interlace) {
462 switch (pass) {
463 case 0:
464 case 1:
465 ypos += 8;
466 break;
467 case 2:
468 ypos += 4;
469 break;
470 case 3:
471 ypos += 2;
472 break;
473 }
474
475 if (ypos >= height) {
476 ++pass;
477 switch (pass) {
478 case 1:
479 ypos = 4;
480 break;
481 case 2:
482 ypos = 2;
483 break;
484 case 3:
485 ypos = 1;
486 break;
487 default:
488 goto fini;
489 }
490 }
491 }
492 else {
493 ++ypos;
494 }
495 dst = image + ypos * data->Width;
496 }
497 if (ypos >= height) {
498 break;
499 }
500 }
501
502 fini:
503
504 if (LWZReadByte( data, false, c ) >= 0) {
505 GIFERRORMSG("too much input data, ignoring extra...");
506 //while (LWZReadByte( data, false, c ) >= 0);
507 }
508
509 return 0;
510 }
511
GIFReset(IDirectFBVideoProvider_GIF_data * data)512 static void GIFReset( IDirectFBVideoProvider_GIF_data *data )
513 {
514 data->transparent = -1;
515 data->delayTime = 1000000; /* default: 1s */
516 data->inputFlag = -1;
517 data->disposal = 0;
518
519 if (data->image)
520 memset( data->image, 0, data->Width*data->Height*4 );
521 }
522
GIFReadHeader(IDirectFBVideoProvider_GIF_data * data)523 static DFBResult GIFReadHeader( IDirectFBVideoProvider_GIF_data *data )
524 {
525 DFBResult ret;
526 u8 buf[7];
527
528 ret = FetchData( data->buffer, buf, 6 );
529 if (ret) {
530 GIFERRORMSG("error reading header");
531 return ret;
532 }
533
534 if (memcmp( buf, "GIF", 3 )) {
535 GIFERRORMSG("bad magic");
536 return DFB_UNSUPPORTED;
537 }
538
539 memcpy( data->Version, &buf[3], 3 );
540 data->Version[3] = '\0';
541
542 ret = FetchData( data->buffer, buf, 7 );
543 if (ret) {
544 GIFERRORMSG("error reading screen descriptor");
545 return ret;
546 }
547
548 data->Width = LM_to_uint( buf[0], buf[1] );
549 data->Height = LM_to_uint( buf[2], buf[3] );
550 data->BitPixel = 2 << (buf[4] & 0x07);
551 data->ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
552 data->Background = buf[5];
553 data->AspectRatio = buf[6];
554 if (data->AspectRatio)
555 data->AspectRatio = ((data->AspectRatio + 15) << 8) >> 6;
556 else
557 data->AspectRatio = (data->Width << 8) / data->Height;
558
559 if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
560 if (ReadColorMap( data->buffer, data->BitPixel, data->ColorMap )) {
561 GIFERRORMSG("error reading global colormap");
562 return DFB_FAILURE;
563 }
564 }
565
566 return DFB_OK;
567 }
568
GIFReadFrame(IDirectFBVideoProvider_GIF_data * data)569 static DFBResult GIFReadFrame( IDirectFBVideoProvider_GIF_data *data )
570 {
571 u8 buf[16], c;
572 int top, left;
573 int width, height;
574 u8 localColorMap[3][MAXCOLORMAPSIZE];
575 bool useGlobalColormap;
576
577 data->curbit = data->lastbit = data->done = data->last_byte = 0;
578
579 data->fresh =
580 data->code_size = data->set_code_size =
581 data->max_code = data->max_code_size =
582 data->firstcode = data->oldcode =
583 data->clear_code = data->end_code = 0;
584
585 for (;;) {
586 DFBResult ret;
587
588 ret = FetchData( data->buffer, &c, 1);
589 if (ret) {
590 GIFERRORMSG("EOF / read error on image data" );
591 return DFB_EOF;
592 }
593
594 if (c == ';') /* GIF terminator */
595 return DFB_EOF;
596
597 if (c == '!') { /* Extension */
598 if (FetchData( data->buffer, &c, 1)) {
599 GIFERRORMSG("EOF / read error on extention function code");
600 return DFB_EOF;
601 }
602 DoExtension( data, c );
603 continue;
604 }
605
606 if (c != ',') { /* Not a valid start character */
607 GIFERRORMSG("bogus character 0x%02x, ignoring", (int) c );
608 continue;
609 }
610
611 ret = FetchData( data->buffer, buf, 9 );
612 if (ret) {
613 GIFERRORMSG("couldn't read left/top/width/height");
614 return ret;
615 }
616
617 left = LM_to_uint( buf[0], buf[1] );
618 top = LM_to_uint( buf[2], buf[3] );
619 width = LM_to_uint( buf[4], buf[5] );
620 height = LM_to_uint( buf[6], buf[7] );
621
622 useGlobalColormap = !BitSet( buf[8], LOCALCOLORMAP );
623
624 if (!useGlobalColormap) {
625 int bitPixel = 2 << (buf[8] & 0x07);
626 if (ReadColorMap( data->buffer, bitPixel, localColorMap ))
627 GIFERRORMSG("error reading local colormap");
628 }
629
630 if (ReadImage( data, left, top, width, height,
631 (useGlobalColormap ?
632 data->ColorMap : localColorMap),
633 BitSet( buf[8], INTERLACE ), 0 )) {
634 GIFERRORMSG("error reading image");
635 return DFB_FAILURE;
636 }
637
638 break;
639 }
640
641 return DFB_OK;
642 }
643
644 /*****************************************************************************/
645
646 static void
IDirectFBVideoProvider_GIF_Destruct(IDirectFBVideoProvider * thiz)647 IDirectFBVideoProvider_GIF_Destruct( IDirectFBVideoProvider *thiz )
648 {
649 IDirectFBVideoProvider_GIF_data *data = thiz->priv;
650
651 thiz->Stop( thiz );
652
653 if (data->image)
654 D_FREE( data->image );
655
656 if (data->buffer)
657 data->buffer->Release( data->buffer );
658
659 pthread_cond_destroy( &data->cond );
660 pthread_mutex_destroy( &data->lock );
661
662 DIRECT_DEALLOCATE_INTERFACE( thiz );
663 }
664
665 static DirectResult
IDirectFBVideoProvider_GIF_AddRef(IDirectFBVideoProvider * thiz)666 IDirectFBVideoProvider_GIF_AddRef( IDirectFBVideoProvider *thiz )
667 {
668 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
669
670 data->ref++;
671
672 return DR_OK;
673 }
674
675 static DirectResult
IDirectFBVideoProvider_GIF_Release(IDirectFBVideoProvider * thiz)676 IDirectFBVideoProvider_GIF_Release( IDirectFBVideoProvider *thiz )
677 {
678 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
679
680 if (--data->ref == 0)
681 IDirectFBVideoProvider_GIF_Destruct( thiz );
682
683 return DR_OK;
684 }
685
686 static DFBResult
IDirectFBVideoProvider_GIF_GetCapabilities(IDirectFBVideoProvider * thiz,DFBVideoProviderCapabilities * caps)687 IDirectFBVideoProvider_GIF_GetCapabilities( IDirectFBVideoProvider *thiz,
688 DFBVideoProviderCapabilities *caps )
689 {
690 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
691
692 if (!caps)
693 return DFB_INVARG;
694
695 *caps = DVCAPS_BASIC | DVCAPS_SCALE | DVCAPS_SPEED;
696
697 return DFB_OK;
698 }
699
700 static DFBResult
IDirectFBVideoProvider_GIF_GetSurfaceDescription(IDirectFBVideoProvider * thiz,DFBSurfaceDescription * desc)701 IDirectFBVideoProvider_GIF_GetSurfaceDescription( IDirectFBVideoProvider *thiz,
702 DFBSurfaceDescription *desc )
703 {
704 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
705
706 if (!desc)
707 return DFB_INVARG;
708
709 desc->flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
710 desc->width = data->Width;
711 desc->height = data->Height;
712 desc->pixelformat = DSPF_ARGB;
713
714 return DFB_OK;
715 }
716
717 static DFBResult
IDirectFBVideoProvider_GIF_GetStreamDescription(IDirectFBVideoProvider * thiz,DFBStreamDescription * desc)718 IDirectFBVideoProvider_GIF_GetStreamDescription( IDirectFBVideoProvider *thiz,
719 DFBStreamDescription *desc )
720 {
721 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
722
723 if (!desc)
724 return DFB_INVARG;
725
726 desc->caps = DVSCAPS_VIDEO;
727
728 snprintf( desc->video.encoding,
729 DFB_STREAM_DESC_ENCODING_LENGTH, "GIF %s", data->Version );
730 desc->video.framerate = 0;
731 desc->video.aspect = (double)data->AspectRatio/256.0;
732 desc->video.bitrate = 0;
733
734 desc->title[0] = desc->author[0] =
735 desc->album[0] = desc->genre[0] = desc->comment[0] = 0;
736 desc->year = 0;
737
738 return DFB_OK;
739 }
740
741 static void*
GIFVideo(DirectThread * self,void * arg)742 GIFVideo( DirectThread *self, void *arg )
743 {
744 IDirectFBVideoProvider_GIF_data *data = arg;
745
746 pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, NULL );
747
748 while (!direct_thread_is_canceled( self )) {
749 DFBResult ret;
750 DFBRectangle rect;
751 DFBRegion clip;
752 CoreSurface *surface;
753 CoreSurfaceBufferLock lock;
754
755 pthread_mutex_lock( &data->lock );
756
757 if (direct_thread_is_canceled( self )) {
758 pthread_mutex_unlock( &data->lock );
759 break;
760 }
761
762 ret = GIFReadFrame( data );
763 if (ret) {
764 if (ret == DFB_EOF) {
765 GIFReset( data );
766 if (data->flags & DVPLAY_LOOPING) {
767 data->buffer->SeekTo( data->buffer, data->start_pos );
768 }
769 else {
770 data->status = DVSTATE_FINISHED;
771 pthread_mutex_unlock( &data->lock );
772 break;
773 }
774 }
775 pthread_mutex_unlock( &data->lock );
776 continue;
777 }
778
779 rect = (data->dst_rect.w == 0)
780 ? data->dst_data->area.wanted : data->dst_rect;
781 dfb_region_from_rectangle( &clip, &data->dst_data->area.current );
782
783 surface = data->dst_data->surface;
784 D_MAGIC_ASSERT( surface, CoreSurface );
785
786 if (dfb_rectangle_region_intersects( &rect, &clip ) &&
787 dfb_surface_lock_buffer( surface, CSBR_BACK, CSAID_CPU, CSAF_WRITE, &lock ) == DFB_OK)
788 {
789 dfb_scale_linear_32( data->image, data->Width, data->Height,
790 lock.addr, lock.pitch, &rect, data->dst_data->surface, &clip );
791
792 dfb_surface_unlock_buffer( surface, &lock );
793
794 if (data->callback)
795 data->callback( data->callback_ctx );
796 }
797
798 if (!data->speed) {
799 pthread_cond_wait( &data->cond, &data->lock );
800 }
801 else {
802 struct timespec ts;
803 struct timeval tv;
804 unsigned long us;
805
806 gettimeofday( &tv, NULL );
807
808 us = data->delayTime;
809 if (data->speed != 1.0)
810 us = ((double)us / data->speed + .5);
811 us += tv.tv_usec;
812
813 ts.tv_sec = tv.tv_sec + us/1000000;
814 ts.tv_nsec = (us%1000000) * 1000;
815
816 pthread_cond_timedwait( &data->cond, &data->lock, &ts );
817 }
818
819 pthread_mutex_unlock( &data->lock );
820 }
821
822 return (void*)0;
823 }
824
825 static DFBResult
IDirectFBVideoProvider_GIF_PlayTo(IDirectFBVideoProvider * thiz,IDirectFBSurface * destination,const DFBRectangle * dest_rect,DVFrameCallback callback,void * ctx)826 IDirectFBVideoProvider_GIF_PlayTo( IDirectFBVideoProvider *thiz,
827 IDirectFBSurface *destination,
828 const DFBRectangle *dest_rect,
829 DVFrameCallback callback,
830 void *ctx )
831 {
832 IDirectFBSurface_data *dst_data;
833 DFBRectangle rect = { 0, 0, 0, 0 };
834 DFBResult ret;
835
836 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
837
838 if (!destination)
839 return DFB_INVARG;
840
841 dst_data = destination->priv;
842 if (!dst_data || !dst_data->surface)
843 return DFB_DESTROYED;
844
845 if (dest_rect) {
846 if (dest_rect->w < 1 || dest_rect->h < 1)
847 return DFB_INVARG;
848
849 rect = *dest_rect;
850 rect.x += dst_data->area.wanted.x;
851 rect.y += dst_data->area.wanted.y;
852 }
853
854 pthread_mutex_lock( &data->lock );
855
856 if (data->status == DVSTATE_FINISHED) {
857 ret = data->buffer->SeekTo( data->buffer, data->start_pos );
858 if (ret) {
859 pthread_mutex_unlock( &data->lock );
860 return ret;
861 }
862 }
863 data->status = DVSTATE_PLAY;
864
865 if (!data->image) {
866 data->image = D_CALLOC( 4, data->Width * data->Height );
867 if (!data->image) {
868 pthread_mutex_unlock( &data->lock );
869 return D_OOM();
870 }
871 }
872
873 if (data->destination)
874 data->destination->Release( data->destination );
875
876 destination->AddRef( destination );
877 data->destination = destination;
878 data->dst_data = dst_data;
879 data->dst_rect = rect;
880
881 data->callback = callback;
882 data->callback_ctx = ctx;
883
884 if (!data->thread) {
885 data->thread = direct_thread_create( DTT_DEFAULT, GIFVideo,
886 (void*)data, "GIF Video" );
887 }
888
889 pthread_mutex_unlock( &data->lock );
890
891 return DFB_OK;
892 }
893
894 static DFBResult
IDirectFBVideoProvider_GIF_Stop(IDirectFBVideoProvider * thiz)895 IDirectFBVideoProvider_GIF_Stop( IDirectFBVideoProvider *thiz )
896 {
897 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
898
899 if (data->thread) {
900 direct_thread_cancel( data->thread );
901 pthread_mutex_lock( &data->lock );
902 pthread_cond_signal( &data->cond );
903 pthread_mutex_unlock( &data->lock );
904 direct_thread_join( data->thread );
905 direct_thread_destroy( data->thread );
906 data->thread = NULL;
907 }
908
909 if (data->destination) {
910 data->destination->Release( data->destination );
911 data->destination = NULL;
912 data->dst_data = NULL;
913 }
914
915 data->status = DVSTATE_STOP;
916
917 return DFB_OK;
918 }
919
920 static DFBResult
IDirectFBVideoProvider_GIF_GetStatus(IDirectFBVideoProvider * thiz,DFBVideoProviderStatus * status)921 IDirectFBVideoProvider_GIF_GetStatus( IDirectFBVideoProvider *thiz,
922 DFBVideoProviderStatus *status )
923 {
924 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
925
926 if (!status)
927 return DFB_INVARG;
928
929 *status = data->status;
930
931 return DFB_OK;
932 }
933
934 static DFBResult
IDirectFBVideoProvider_GIF_SeekTo(IDirectFBVideoProvider * thiz,double seconds)935 IDirectFBVideoProvider_GIF_SeekTo( IDirectFBVideoProvider *thiz,
936 double seconds )
937 {
938 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
939
940 if (seconds < 0.0)
941 return DFB_INVARG;
942
943 return DFB_UNSUPPORTED;
944 }
945
946 static DFBResult
IDirectFBVideoProvider_GIF_GetPos(IDirectFBVideoProvider * thiz,double * seconds)947 IDirectFBVideoProvider_GIF_GetPos( IDirectFBVideoProvider *thiz,
948 double *seconds )
949 {
950 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
951
952 if (!seconds)
953 return DFB_INVARG;
954
955 *seconds = 0.0;
956
957 return DFB_UNSUPPORTED;
958 }
959
960 static DFBResult
IDirectFBVideoProvider_GIF_GetLength(IDirectFBVideoProvider * thiz,double * seconds)961 IDirectFBVideoProvider_GIF_GetLength( IDirectFBVideoProvider *thiz,
962 double *seconds )
963 {
964 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
965
966 if (!seconds)
967 return DFB_INVARG;
968
969 *seconds = 0.0;
970
971 return DFB_UNSUPPORTED;
972 }
973
974 static DFBResult
IDirectFBVideoProvider_GIF_SetPlaybackFlags(IDirectFBVideoProvider * thiz,DFBVideoProviderPlaybackFlags flags)975 IDirectFBVideoProvider_GIF_SetPlaybackFlags( IDirectFBVideoProvider *thiz,
976 DFBVideoProviderPlaybackFlags flags )
977 {
978 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
979
980 if (flags & ~DVPLAY_LOOPING)
981 return DFB_UNSUPPORTED;
982
983 if (flags & DVPLAY_LOOPING && !data->seekable)
984 return DFB_UNSUPPORTED;
985
986 data->flags = flags;
987
988 return DFB_OK;
989 }
990
991 static DFBResult
IDirectFBVideoProvider_GIF_SetSpeed(IDirectFBVideoProvider * thiz,double multiplier)992 IDirectFBVideoProvider_GIF_SetSpeed( IDirectFBVideoProvider *thiz,
993 double multiplier )
994 {
995 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
996
997 if (multiplier < 0.0)
998 return DFB_INVARG;
999
1000 if (data->speed != multiplier) {
1001 pthread_mutex_lock( &data->lock );
1002 data->speed = multiplier;
1003 pthread_cond_signal( &data->cond );
1004 pthread_mutex_unlock( &data->lock );
1005 }
1006
1007 return DFB_OK;
1008 }
1009
1010 static DFBResult
IDirectFBVideoProvider_GIF_GetSpeed(IDirectFBVideoProvider * thiz,double * multiplier)1011 IDirectFBVideoProvider_GIF_GetSpeed( IDirectFBVideoProvider *thiz,
1012 double *multiplier )
1013 {
1014 DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
1015
1016 if (!multiplier)
1017 return DFB_INVARG;
1018
1019 *multiplier = data->speed;
1020
1021 return DFB_OK;
1022 }
1023
1024 /* exported symbols */
1025 static DFBResult
Probe(IDirectFBVideoProvider_ProbeContext * ctx)1026 Probe( IDirectFBVideoProvider_ProbeContext *ctx )
1027 {
1028 if (!memcmp( ctx->header, "GIF89", 5 ))
1029 return DFB_OK;
1030
1031 return DFB_UNSUPPORTED;
1032 }
1033
1034 static DFBResult
Construct(IDirectFBVideoProvider * thiz,IDirectFBDataBuffer * buffer)1035 Construct( IDirectFBVideoProvider *thiz,
1036 IDirectFBDataBuffer *buffer )
1037 {
1038 DFBResult ret;
1039
1040 DIRECT_ALLOCATE_INTERFACE_DATA( thiz, IDirectFBVideoProvider_GIF )
1041
1042 data->ref = 1;
1043 data->status = DVSTATE_STOP;
1044 data->buffer = buffer;
1045 data->speed = 1.0;
1046
1047 buffer->AddRef( buffer );
1048 data->seekable = (buffer->SeekTo( buffer, 0 ) == DFB_OK);
1049
1050 GIFReset( data );
1051 ret = GIFReadHeader( data );
1052 if (ret) {
1053 IDirectFBVideoProvider_GIF_Destruct( thiz );
1054 return ret;
1055 }
1056
1057 data->buffer->GetPosition( data->buffer, &data->start_pos );
1058
1059 direct_util_recursive_pthread_mutex_init( &data->lock );
1060 pthread_cond_init( &data->cond, NULL );
1061
1062 thiz->AddRef = IDirectFBVideoProvider_GIF_AddRef;
1063 thiz->Release = IDirectFBVideoProvider_GIF_Release;
1064 thiz->GetCapabilities = IDirectFBVideoProvider_GIF_GetCapabilities;
1065 thiz->GetSurfaceDescription = IDirectFBVideoProvider_GIF_GetSurfaceDescription;
1066 thiz->GetStreamDescription = IDirectFBVideoProvider_GIF_GetStreamDescription;
1067 thiz->PlayTo = IDirectFBVideoProvider_GIF_PlayTo;
1068 thiz->Stop = IDirectFBVideoProvider_GIF_Stop;
1069 thiz->GetStatus = IDirectFBVideoProvider_GIF_GetStatus;
1070 thiz->SeekTo = IDirectFBVideoProvider_GIF_SeekTo;
1071 thiz->GetPos = IDirectFBVideoProvider_GIF_GetPos;
1072 thiz->GetLength = IDirectFBVideoProvider_GIF_GetLength;
1073 thiz->SetPlaybackFlags = IDirectFBVideoProvider_GIF_SetPlaybackFlags;
1074 thiz->SetSpeed = IDirectFBVideoProvider_GIF_SetSpeed;
1075 thiz->GetSpeed = IDirectFBVideoProvider_GIF_GetSpeed;
1076
1077 return DFB_OK;
1078 }
1079