4 Part of the swftools package.
6 Copyright (c) 2003 Matthias Kramm <kramm@quiss.org> */
12 #include "../lib/rfxswf.h"
14 #include "h263tables.c"
21 typedef struct _VIDEOSTREAM
31 void swf_SetVideoStreamDefine(TAG*tag, VIDEOSTREAM*stream, U16 frames, U16 width, U16 height)
33 width=width&~15; height=height&~15;
34 swf_SetU16(tag, frames);
35 swf_SetU16(tag, width);
36 swf_SetU16(tag, height);
37 swf_SetU8(tag, 1); /* smoothing on */
38 swf_SetU8(tag, 2); /* codec = h.263 sorenson spark */
40 memset(stream, 0, sizeof(VIDEOSTREAM));
41 stream->linex = width;
44 stream->width = width;
45 stream->height = height;
46 stream->current = (YUV*)malloc(width*height*sizeof(YUV));
47 stream->oldpic = (YUV*)malloc(width*height*sizeof(YUV));
49 memset(stream->oldpic, 0, width*height*sizeof(YUV));
52 typedef struct _block_t
62 typedef struct _fblock_t
72 static int zigzagtable[64] = {
73 0, 1, 5, 6, 14, 15, 27, 28,
74 2, 4, 7, 13, 16, 26, 29, 42,
75 3, 8, 12, 17, 25, 30, 41, 43,
76 9, 11, 18, 24, 31, 40, 44, 53,
77 10, 19, 23, 32, 39, 45, 52, 54,
78 20, 22, 33, 38, 46, 51, 55, 60,
79 21, 34, 37, 47, 50, 56, 59, 61,
80 35, 36, 48, 49, 57, 58, 62, 63};
82 static void fzigzag(double*src)
87 ((int*)&tmp[zigzagtable[t]])[0] = ((int*)&src[t])[0];
88 ((int*)&tmp[zigzagtable[t]])[1] = ((int*)&src[t])[1];
90 memcpy(src, tmp, sizeof(double)*64);
93 #define PI 3.14159265358979
94 #define SQRT2 1.414214
95 #define RSQRT2 (1.0/1.414214)
97 static double table[8][8] =
99 {0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548},
100 {0.980785280403230,0.831469612302545,0.555570233019602,0.195090322016128,-0.195090322016128,-0.555570233019602,-0.831469612302545,-0.980785280403230},
101 {0.923879532511287,0.382683432365090,-0.382683432365090,-0.923879532511287,-0.923879532511287,-0.382683432365090,0.382683432365090,0.923879532511287},
102 {0.831469612302545,-0.195090322016128,-0.980785280403230,-0.555570233019602,0.555570233019602,0.980785280403230,0.195090322016129,-0.831469612302545},
103 {0.707106781186548,-0.707106781186547,-0.707106781186548,0.707106781186547,0.707106781186548,-0.707106781186547,-0.707106781186547,0.707106781186547},
104 {0.555570233019602,-0.980785280403230,0.195090322016128,0.831469612302545,-0.831469612302545,-0.195090322016128,0.980785280403231,-0.555570233019602},
105 {0.382683432365090,-0.923879532511287,0.923879532511287,-0.382683432365090,-0.382683432365091,0.923879532511287,-0.923879532511286,0.382683432365090},
106 {0.195090322016128,-0.555570233019602,0.831469612302545,-0.980785280403231,0.980785280403230,-0.831469612302545,0.555570233019602,-0.195090322016129}
109 static void dct(double*src)
120 c+=table[u][x]*src[v*8+x];
130 c+=table[v][y]*tmp[y*8+u];
136 static void idct(double*src)
146 c+=table[u][x]*src[y*8+u];
156 c+=table[v][y]*tmp[v*8+x];
162 static inline int truncate256(int a)
164 if(a>255) return 255;
169 static void getregion(fblock_t* bb, YUV*pic, int bx, int by, int linex)
171 YUV*p1 = &pic[by*linex*16+bx*16];
173 int y1=0, y2=0, y3=0, y4=0;
178 bb->u[u++] = (p2[x*2].u + p2[x*2+1].u + p2[linex+x*2].u + p2[linex+x*2+1].u)/4;
179 bb->v[v++] = (p2[x*2].v + p2[x*2+1].v + p2[linex+x*2].v + p2[linex+x*2+1].v)/4;
180 bb->y1[y1++] = p1[x].y;
181 bb->y2[y2++] = p1[x+8].y;
182 bb->y3[y3++] = p1[linex*8+x].y;
183 bb->y4[y4++] = p1[linex*8+x+8].y;
189 static void rgb2yuv(YUV*dest, RGBA*src, int linex, int width, int height)
192 for(y=0;y<height;y++) {
193 for(x=0;x<width;x++) {
195 r = src[y*linex+x].r;
196 g = src[y*linex+x].g;
197 b = src[y*linex+x].b;
198 dest[y*linex+x].y = (r*0.299 + g*0.587 + b*0.114);
199 dest[y*linex+x].u = (r*-0.169 + g*-0.332 + b*0.500 + 128.0);
200 dest[y*linex+x].v = (r*0.500 + g*-0.419 + b*-0.0813 + 128.0);
204 static void copyregion(VIDEOSTREAM*s, YUV*dest, YUV*src, int bx, int by)
206 YUV*p1 = &src[by*s->linex*16+bx*16];
207 YUV*p2 = &dest[by*s->linex*16+bx*16];
210 memcpy(p1, p2, 16*sizeof(YUV));
211 p1+=s->linex;p2+=s->linex;
215 static void yuv2rgb(RGBA*dest, YUV*src, int linex, int width, int height)
218 for(y=0;y<height;y++) {
219 for(x=0;x<width;x++) {
221 u = src[y*linex+x].u;
222 v = src[y*linex+x].v;
223 yy = src[y*linex+x].y;
224 dest[y*linex+x].r = truncate256(yy + ((360*(v-128))>>8));
225 dest[y*linex+x].g = truncate256(yy - ((88*(u-128)+183*(v-128))>>8));
226 dest[y*linex+x].b = truncate256(yy + ((455 * (u-128))>>8));
230 static void copyblock(VIDEOSTREAM*s, YUV*dest, block_t*b, int bx, int by)
232 YUV*p1 = &dest[(by*16)*s->linex+bx*16];
233 YUV*p2 = &dest[(by*16+8)*s->linex+bx*16];
238 p1[x+0].u = b->u[(y/2)*8+(x/2)];
239 p1[x+0].v = b->v[(y/2)*8+(x/2)];
240 p1[x+0].y = b->y1[y*8+x];
241 p1[x+8].u = b->u[(y/2)*8+(x/2)+4];
242 p1[x+8].v = b->v[(y/2)*8+(x/2)+4];
243 p1[x+8].y = b->y2[y*8+x];
244 p2[x+0].u = b->u[(y/2+4)*8+(x/2)];
245 p2[x+0].v = b->v[(y/2+4)*8+(x/2)];
246 p2[x+0].y = b->y3[y*8+x];
247 p2[x+8].u = b->u[(y/2+4)*8+(x/2)+4];
248 p2[x+8].v = b->v[(y/2+4)*8+(x/2)+4];
249 p2[x+8].y = b->y4[y*8+x];
256 static int compareregions(VIDEOSTREAM*s, int bx, int by)
258 int linex = s->width;
259 YUV*p1 = &s->current[by*linex*16+bx*16];
260 YUV*p2 = &s->oldpic[by*linex*16+bx*16];
270 diff += y*y+(u*u+v*v)/4;
278 static int valtodc(int val)
286 /* TODO: what to do for zero values? skip the block? */
295 static int dctoval(int dc)
308 static int codehuffman(TAG*tag, struct huffcode*table, int index)
310 /* TODO: !optimize! */
312 while(table[index].code[i]) {
313 if(table[index].code[i]=='0')
314 swf_SetBits(tag, 0, 1);
316 swf_SetBits(tag, 1, 1);
322 static void quantize8x8(double*src, int*dest, int has_dc, int quant)
326 dest[0] = valtodc((int)src[0]); /*DC*/
331 dest[t] = (int)src[t];
332 /* exact: if(quant&1){dest[t] = (dest[t]/quant - 1)/2;}else{dest[t] = ((dest[t]+1)/quant - 1)/2;} */
333 //if(quant&1){dest[t] = (dest[t]/quant - 1)/2;}else{dest[t] = ((dest[t]+1)/quant - 1)/2;}
334 dest[t] = dest[t]/(quant*2);
338 static void dequantize8x8(int*b, int has_dc, int quant)
342 b[0] = dctoval(b[0]); //DC
345 for(t=pos;t<64;t++) {
354 b[t] = quant*(2*b[t]+1); //-7,8,24,40
356 b[t] = quant*(2*b[t]+1)-1; //-8,7,23,39
363 /* paragraph 6.2.2, "clipping of reconstruction levels": */
364 if(b[t]>2047) b[t]=2047;
365 if(b[t]<-2048) b[t]=-2048;
369 static int hascoef(int*b, int has_dc)
375 for(t=pos;t<64;t++) {
382 static void encode8x8(TAG*tag, int*bb, int has_dc, int has_tcoef)
388 swf_SetBits(tag, bb[0], 8);
394 /* determine last non-null coefficient */
395 for(last=63;last>=pos;last--) {
396 /* TODO: we could leave out small coefficients
397 after a certain point (32?) */
401 /* blocks without coefficients should not be included
402 in the cbpy/cbpc patterns: */
411 while(!bb[pos] && pos<last) {
423 for(t=0;t<RLE_ESCAPE;t++) {
424 if(rle_params[t].run == run &&
425 rle_params[t].level == level &&
426 rle_params[t].last == islast) {
427 codehuffman(tag, rle, t);
428 swf_SetBits(tag, sign, 1);
433 codehuffman(tag, rle, RLE_ESCAPE);
440 swf_SetBits(tag, islast, 1);
441 swf_SetBits(tag, run, 6);
442 swf_SetBits(tag, level, 8); //FIXME: fixme??
452 static void dodct(fblock_t*fb)
455 dct(fb->y1); dct(fb->y2); dct(fb->y3); dct(fb->y4);
456 dct(fb->u); dct(fb->v);
465 static void doidct(block_t*b)
470 fb.y1[t] = b->y1[zigzagtable[t]];
471 fb.y2[t] = b->y2[zigzagtable[t]];
472 fb.y3[t] = b->y3[zigzagtable[t]];
473 fb.y4[t] = b->y4[zigzagtable[t]];
474 fb.u[t] = b->u[zigzagtable[t]];
475 fb.v[t] = b->v[zigzagtable[t]];
477 idct(fb.y1); idct(fb.y2); idct(fb.y3); idct(fb.y4);
478 idct(fb.u); idct(fb.v);
488 static void truncateblock(block_t*b)
492 b->y1[t] = truncate256(b->y1[t]);
493 b->y2[t] = truncate256(b->y2[t]);
494 b->y3[t] = truncate256(b->y3[t]);
495 b->y4[t] = truncate256(b->y4[t]);
496 b->u[t] = truncate256(b->u[t]);
497 b->v[t] = truncate256(b->v[t]);
501 static void quantize(fblock_t*fb, block_t*b, int has_dc, int quant)
503 quantize8x8(fb->y1, b->y1, has_dc, quant);
504 quantize8x8(fb->y2, b->y2, has_dc, quant);
505 quantize8x8(fb->y3, b->y3, has_dc, quant);
506 quantize8x8(fb->y4, b->y4, has_dc, quant);
507 quantize8x8(fb->u, b->u, has_dc, quant);
508 quantize8x8(fb->v, b->v, has_dc, quant);
510 static void dequantize(block_t*b, int has_dc, int quant)
512 dequantize8x8(b->y1, has_dc, quant);
513 dequantize8x8(b->y2, has_dc, quant);
514 dequantize8x8(b->y3, has_dc, quant);
515 dequantize8x8(b->y4, has_dc, quant);
516 dequantize8x8(b->u, has_dc, quant);
517 dequantize8x8(b->v, has_dc, quant);
520 static void getblockpatterns(block_t*b, int*cbpybits,int*cbpcbits, int has_dc)
525 *cbpybits|=hascoef(b->y1, has_dc)*8;
526 *cbpybits|=hascoef(b->y2, has_dc)*4;
527 *cbpybits|=hascoef(b->y3, has_dc)*2;
528 *cbpybits|=hascoef(b->y4, has_dc)*1;
530 *cbpcbits|=hascoef(b->u, has_dc)*2;
531 *cbpcbits|=hascoef(b->v, has_dc)*1;
534 static void setQuant(TAG*tag, int dquant)
541 swf_SetBits(tag, 0x0, 2);
542 } else if(dquant == -2) {
543 swf_SetBits(tag, 0x1, 2);
544 } else if(dquant == +1) {
545 swf_SetBits(tag, 0x2, 2);
546 } else if(dquant == +2) {
547 swf_SetBits(tag, 0x3, 2);
549 assert(0*strlen("invalid dquant"));
553 static void change_quant(int quant, int*dquant)
559 static void encode_blockI(TAG*tag, VIDEOSTREAM*s, int bx, int by, int*quant)
564 int cbpcbits = 0, cbpybits=0;
566 getregion(&fb, s->current, bx, by, s->width);
569 change_quant(*quant, &dquant);
571 quantize(&fb, &b, 1, *quant);
573 //decode_blockI(s, &b, bx, by);
575 getblockpatterns(&b, &cbpybits, &cbpcbits, 1);
578 codehuffman(tag, mcbpc_intra, 4+cbpcbits);
580 codehuffman(tag, mcbpc_intra, 0+cbpcbits);
583 codehuffman(tag, cbpy, cbpybits);
586 setQuant(tag, dquant);
590 encode8x8(tag, b.y1, 1, cbpybits&8);
591 encode8x8(tag, b.y2, 1, cbpybits&4);
592 encode8x8(tag, b.y3, 1, cbpybits&2);
593 encode8x8(tag, b.y4, 1, cbpybits&1);
596 encode8x8(tag, b.u, 1, cbpcbits&2);
597 encode8x8(tag, b.v, 1, cbpcbits&1);
600 dequantize(&b, 1, *quant);
603 copyblock(s, s->current, &b, bx, by);
606 static void yuvdiff(fblock_t*a, fblock_t*b)
610 a->y1[t] = (a->y1[t] - b->y1[t]);
611 a->y2[t] = (a->y2[t] - b->y2[t]);
612 a->y3[t] = (a->y3[t] - b->y3[t]);
613 a->y4[t] = (a->y4[t] - b->y4[t]);
614 a->u[t] = (a->u[t] - b->u[t]);
615 a->v[t] = (a->v[t] - b->v[t]);
619 static void encode_blockP(TAG*tag, VIDEOSTREAM*s, int bx, int by, int*quant)
621 fblock_t fb,fbdiff, fbold;
628 int cbpcbits = 0, cbpybits=0;
631 diff = compareregions(s, bx, by);
632 if(diff < 24 /*TODO: should be a parameter- good values are between 32 and 48 */) {
633 swf_SetBits(tag, 1,1); /* cod=1, block skipped */
634 copyregion(s, s->current, s->oldpic, bx, by);
638 getregion(&fb, s->current, bx, by, s->width);
643 /* mvd (0,0) block (mode=0) */
645 memcpy(&fbdiff, &fb, sizeof(fblock_t));
646 getregion(&fbold, s->oldpic, bx, by, s->linex);
647 yuvdiff(&fbdiff, &fbold);
648 //getregiondiff(&fbdiff, s->oldpic, s->current, bx, by, s->linex);
652 quantize(&fbdiff, &b, has_dc, *quant);
653 getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc);
654 mode = 0; // mvd w/o mvd24
655 swf_SetBits(tag,0,1); // COD
656 codehuffman(tag, mcbpc_inter, mode*4+cbpcbits);
657 codehuffman(tag, cbpy, cbpybits^15);
660 codehuffman(tag, mvd, 32);
661 codehuffman(tag, mvd, 32);
664 encode8x8(tag, b.y1, has_dc, cbpybits&8);
665 encode8x8(tag, b.y2, has_dc, cbpybits&4);
666 encode8x8(tag, b.y3, has_dc, cbpybits&2);
667 encode8x8(tag, b.y4, has_dc, cbpybits&1);
670 encode8x8(tag, b.u, has_dc, cbpcbits&2);
671 encode8x8(tag, b.v, has_dc, cbpcbits&1);
673 /* -- reconstruction -- */
674 dequantize(&b, 0, *quant);
678 b.y1[t] = truncate256(b.y1[t] + (int)fbold.y1[t]);
679 b.y2[t] = truncate256(b.y2[t] + (int)fbold.y2[t]);
680 b.y3[t] = truncate256(b.y3[t] + (int)fbold.y3[t]);
681 b.y4[t] = truncate256(b.y4[t] + (int)fbold.y4[t]);
682 b.u[t] = truncate256(b.u[t] + (int)fbold.u[t]);
683 b.v[t] = truncate256(b.v[t] + (int)fbold.v[t]);
685 copyblock(s, s->current, &b, bx, by);
688 /* i block (mode=3) */
692 quantize(&fb, &b, has_dc, *quant);
693 getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc);
694 swf_SetBits(tag,0,1); // COD
695 codehuffman(tag, mcbpc_inter, mode*4+cbpcbits);
696 codehuffman(tag, cbpy, cbpybits);
699 encode8x8(tag, b.y1, has_dc, cbpybits&8);
700 encode8x8(tag, b.y2, has_dc, cbpybits&4);
701 encode8x8(tag, b.y3, has_dc, cbpybits&2);
702 encode8x8(tag, b.y4, has_dc, cbpybits&1);
705 encode8x8(tag, b.u, has_dc, cbpcbits&2);
706 encode8x8(tag, b.v, has_dc, cbpcbits&1);
708 dequantize(&b, 1, *quant);
711 copyblock(s, s->current, &b, bx, by);
718 quantize(&fb, &b, has_dc, *quant);
719 getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc);
721 if(!dquant && has_mvd && !has_mvd24 && !has_dc) mode = 0;
722 else if(dquant && has_mvd && !has_mvd24 && !has_dc) mode = 1;
723 else if(!dquant && has_mvd && has_mvd24 && !has_dc) mode = 2;
724 else if(!dquant && !has_mvd && !has_mvd24 && has_dc) mode = 3;
725 else if(dquant && !has_mvd && !has_mvd24 && has_dc) mode = 4;
728 swf_SetBits(tag,0,1); /* cod - 1 if we're not going to code this block*/
730 codehuffman(tag, mcbpc_inter, mode*4+cbpcbits);
731 codehuffman(tag, cbpy, (mode==3 || mode==4)?cbpybits:cbpybits^15);
734 setQuant(tag, dquant);
739 codehuffman(tag, mvd, 32);
740 codehuffman(tag, mvd, 32);
746 encode8x8(tag, b.y1, has_dc, cbpybits&8);
747 encode8x8(tag, b.y2, has_dc, cbpybits&4);
748 encode8x8(tag, b.y3, has_dc, cbpybits&2);
749 encode8x8(tag, b.y4, has_dc, cbpybits&1);
752 encode8x8(tag, b.u, has_dc, cbpcbits&2);
753 encode8x8(tag, b.v, has_dc, cbpcbits&1);
757 #define TYPE_IFRAME 0
758 #define TYPE_PFRAME 1
760 static void writeHeader(TAG*tag, int width, int height, int frame, int quant, int type)
763 swf_SetU16(tag, frame);
764 swf_SetBits(tag, 1, 17); /* picture start code*/
765 swf_SetBits(tag, 0, 5); /* version=0, version 1 would optimize rle behaviour*/
766 swf_SetBits(tag, frame, 8); /* time reference */
768 /* write dimensions, taking advantage of some predefined sizes
769 if the opportunity presents itself */
770 i32 = width<<16|height;
773 case 352<<16|288: swf_SetBits(tag, 2, 3);break;
774 case 176<<16|144: swf_SetBits(tag, 3, 3);break;
775 case 128<<16|96: swf_SetBits(tag, 4, 3);break;
776 case 320<<16|240: swf_SetBits(tag, 5, 3);break;
777 case 160<<16|120: swf_SetBits(tag, 6, 3);break;
779 if(width>255 || height>255) {
780 swf_SetBits(tag, 1, 3);
781 swf_SetBits(tag, width, 16);
782 swf_SetBits(tag, height, 16);
784 swf_SetBits(tag, 0, 3);
785 swf_SetBits(tag, width, 8);
786 swf_SetBits(tag, height, 8);
790 swf_SetBits(tag, type, 2); /* I-Frame or P-Frame */
791 swf_SetBits(tag, 0, 1); /* No deblock filter */
793 swf_SetBits(tag, quant, 5); /* quantizer (1-31), may be updated later on*/
794 swf_SetBits(tag, 0, 1); /* No extra info */
797 void swf_SetVideoStreamIFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic)
799 int bx, by, bbx, bby;
802 writeHeader(tag, s->width, s->height, s->frame, quant, TYPE_IFRAME);
804 bbx = (s->width+15)/16; //TODO: move bbx,bby into VIDEOSTREAM
805 bby = (s->height+15)/16;
807 rgb2yuv(s->current, pic, s->linex, s->width, s->height);
809 for(by=0;by<bby;by++)
811 for(bx=0;bx<bbx;bx++)
813 encode_blockI(tag, s, bx, by, &quant);
817 memcpy(s->oldpic, s->current, s->width*s->height*sizeof(YUV));
820 void swf_SetVideoStreamPFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic)
822 int bx, by, bbx, bby;
825 writeHeader(tag, s->width, s->height, s->frame, quant, TYPE_PFRAME);
827 bbx = (s->width+15)/16;
828 bby = (s->height+15)/16;
830 rgb2yuv(s->current, pic, s->linex, s->width, s->height);
832 for(by=0;by<bby;by++)
834 for(bx=0;bx<bbx;bx++)
836 encode_blockP(tag, s, bx, by, &quant);
840 memcpy(s->oldpic, s->current, s->width*s->height*sizeof(YUV));
844 FILE*fi = fopen("test.ppm", "wb");
845 yuv2rgb(pic, s->current, s->linex, s->width, s->height);
846 fprintf(fi, "P6\n%d %d\n255\n", s->width, s->height);
847 for(t=0;t<s->width*s->height;t++)
849 fwrite(&pic[t].r, 1, 1, fi);
850 fwrite(&pic[t].g, 1, 1, fi);
851 fwrite(&pic[t].b, 1, 1, fi);
857 int main(int argn, char*argv[])
863 RGBA* pic, *pic2, rgb;
870 char* fname = "/home/kramm/pics/peppers.png";
874 memset(&stream, 0, sizeof(stream));
876 getPNG(fname, &width, &height, &data);
877 pic = (RGBA*)malloc(width*height*sizeof(RGBA));
878 pic2 = (RGBA*)malloc(width*height*sizeof(RGBA));
879 memcpy(pic, data, width*height*sizeof(RGBA));
882 printf("Compressing %s, size %dx%d\n", fname, width, height);
884 memset(&swf,0,sizeof(SWF));
885 memset(&obj,0,sizeof(obj));
888 swf.frameRate = framerate*256;
889 swf.movieSize.xmax = 20*width;
890 swf.movieSize.ymax = 20*height;
892 swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
894 rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
895 swf_SetRGB(tag,&rgb);
897 tag = swf_InsertTag(tag, ST_DEFINEVIDEOSTREAM);
899 swf_SetVideoStreamDefine(tag, &stream, frames, width, height);
901 for(t=0;t<frames;t++)
905 for(y=0,yy=0;y<height;y++,yy+=d) {
906 RGBA*line = &pic[((int)yy)*width];
907 for(x=0,xx=0;x<width;x++,xx+=d) {
908 pic2[y*width+x] = line[((int)xx)];
911 printf("frame:%d\n", t);fflush(stdout);
913 tag = swf_InsertTag(tag, ST_VIDEOFRAME);
916 swf_SetVideoStreamIFrame(tag, &stream, pic2);
918 swf_SetVideoStreamPFrame(tag, &stream, pic2);
920 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
921 swf_GetPlaceObject(0, &obj);
930 swf_SetPlaceObject(tag,&obj);
932 tag = swf_InsertTag(tag, ST_SHOWFRAME);
936 tag = swf_InsertTag(tag, ST_END);
938 fi = open("video3.swf", O_WRONLY|O_CREAT|O_TRUNC, 0644);
939 if(swf_WriteSWC(fi,&swf)<0) {
940 fprintf(stderr,"WriteSWF() failed.\n");