new method gfxbbox_transform
[swftools.git] / avi2swf / v2swf.c
blobc974bfbacdb286e546c9c46f56aedd872641201b
1 /* v2swf.c
2 part of swftools
4 Copyright (C) 2003 Matthias Kramm <kramm@quiss.org>
6 This program 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.
11 This program 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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include "v2swf.h"
24 #include "../lib/rfxswf.h"
25 #include "../lib/q.h"
27 typedef struct _v2swf_internal_t
29 TAG*tag;
31 int filesize;
32 int headersize;
33 int frames;
35 int myframes;
37 writer_t out;
38 writer_t out2;
40 ringbuffer_t r;
41 videoreader_t* video;
42 double video_fps;
44 int width;
45 int height;
47 int video_eof;
48 int audio_eof;
50 unsigned char* vrbuffer;
51 unsigned char* buffer;
52 unsigned char* lastbitmap;
54 int id;
55 int lastid;
57 int quality;
58 int blockdiff;
59 int keyframe_interval;
60 int diffmode;
62 float framerate;
63 float fpsratio;
64 float fpspos;
66 int bitrate;
67 int samplerate;
69 int finished;
70 int keyframe;
71 int showframe;
73 int skipframes;
75 float samplepos;
76 float framesamplepos;
77 int samplewritepos;
78 double soundframepos;
79 int soundstreamhead;
80 int seek;
82 int numframes;
84 double audio_fix;
85 int fixheader;
86 int prescale;
88 int scale;
90 int add_cut;
92 int domotion;
94 int head_done;
96 int version;
98 VIDEOSTREAM stream;
100 } v2swf_internal_t;
102 static int verbose = 0;
103 static int filelog = 0;
105 static void msg(char*format, ...)
107 char buf[1024];
108 int l;
109 va_list arglist;
110 if(!verbose)
111 return;
112 va_start(arglist, format);
113 vsnprintf(buf, sizeof(buf)-1, format, arglist);
114 va_end(arglist);
115 l = strlen(buf);
116 while(l && buf[l-1]=='\n') {
117 buf[l-1] = 0;
118 l--;
120 if(filelog)
122 FILE*fi = fopen("debug.log", "ab+");
123 fprintf(fi, "(v2swf) %s\n", buf);
124 fflush(fi);
125 fclose(fi);
128 printf("(v2swf) %s\n", buf);
129 fflush(stdout);
132 extern int swf_mp3_in_samplerate;
133 extern int swf_mp3_out_samplerate;
134 extern int swf_mp3_channels;
135 extern int swf_mp3_bitrate;
138 static void writeShape(v2swf_internal_t*i, int id, int gfxid, int width, int height)
140 RGBA rgb;
141 MATRIX m;
142 SHAPE*shape;
143 SRECT r;
144 int lines = 0;
145 int ls,fs;
146 swf_ResetTag(i->tag, ST_DEFINESHAPE);
147 swf_ShapeNew(&shape);
148 rgb.b = rgb.g = rgb.r = 0xff;
149 if(lines)
150 ls = swf_ShapeAddLineStyle(shape,20,&rgb);
151 swf_GetMatrix(NULL,&m);
152 m.sx = 20*65536;
153 m.sy = 20*65536;
155 fs = swf_ShapeAddBitmapFillStyle(shape,&m,gfxid,0);
156 swf_SetU16(i->tag,id); // ID
157 r.xmin = 0;
158 r.ymin = 0;
159 r.xmax = width*20;
160 r.ymax = height*20;
161 swf_SetRect(i->tag,&r);
163 swf_SetShapeStyles(i->tag,shape);
164 swf_ShapeCountBits(shape,NULL,NULL);
165 swf_SetShapeBits(i->tag,shape);
167 swf_ShapeSetAll(i->tag,shape,0,0,lines?ls:0,fs,0);
169 swf_ShapeSetLine(i->tag,shape,width*20,0);
170 swf_ShapeSetLine(i->tag,shape,0,height*20);
171 swf_ShapeSetLine(i->tag,shape,-width*20,0);
172 swf_ShapeSetLine(i->tag,shape,0,-height*20);
173 swf_ShapeSetEnd(i->tag);
174 i->filesize += swf_WriteTag2(&i->out, i->tag);
175 swf_ShapeFree(shape);
178 /* returns 0 on partial read */
179 static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
181 double pos = 0;
182 double ratio = (double) video->samplerate * speedup / swf_mp3_in_samplerate;
183 int rlen = (int)(len * ratio);
184 int t;
185 S16 tmp[576*32];
186 int r = /*resampled len */ rlen *
187 /* s16_le */ 2 *
188 video->channels;
189 int l = 0;
190 memset(tmp, 0, sizeof(tmp));
191 if(r>0)
192 l = videoreader_getsamples(video, tmp, r);
193 if(l <= 0) {
194 return 0;
196 msg("%d samples read", l);
198 /* convert to 1 channel */
199 for(t=0;t<rlen;t++) {
200 int s;
201 int a=0;
202 for(s=0;s<video->channels;s++)
203 a += tmp[t*video->channels+s];
204 tmp[t] = a/video->channels;
207 /* down/up-sample to the desired input samplerate (swf_mp3_in_samplerate) */
208 for(t=0;t<len;t++) {
209 data[t] = tmp[(int)pos];
210 pos+=ratio;
212 return l == r;
215 static void writeAudioForOneFrame(v2swf_internal_t* i)
217 int blocksize;
218 double blockspersecond;
219 double framespersecond, framesperblock, samplesperframe, samplesperblock;
220 int seek;
221 int s;
222 double speedup = i->audio_fix;
223 int num = 0;
224 int pos = 0;
225 S16 block1[576*4 * 2];
227 msg("writeAudioForOneFrame()");
229 if(i->audio_eof || i->video->channels<=0 || i->video->samplerate<=0) {
230 i->audio_eof = 1;
231 return; /* no sound in video */
234 blocksize = (i->samplerate > 22050) ? 1152 : 576;
235 blockspersecond = ((double)i->samplerate)/blocksize;
237 /* notice: for framerates greater than about 35, audio starts getting choppy. */
238 framespersecond = i->framerate;
240 framesperblock = framespersecond / blockspersecond;
241 samplesperframe = (blocksize * blockspersecond) / framespersecond; /* 11khz-samples per frame */
242 samplesperblock = samplesperframe * framesperblock;
244 msg("samplesperblock: %f", samplesperblock);
246 if(!i->soundstreamhead) {
247 swf_mp3_out_samplerate = i->samplerate;
248 /* The pre-processing of sound samples in getSamples(..) above
249 re-samples the sound to swf_mp3_in_samplerate. It is best to
250 simply make it the original samplerate: */
251 swf_mp3_in_samplerate = i->video->samplerate;
253 /* first run - initialize */
254 swf_mp3_channels = 1;//i->video->channels;
255 swf_mp3_bitrate = i->bitrate;
256 swf_ResetTag(i->tag, ST_SOUNDSTREAMHEAD);
257 /* samplesperframe overrides the movie framerate: */
258 msg("swf_SetSoundStreamHead(): %08x %d", i->tag, samplesperframe);
259 swf_SetSoundStreamHead(i->tag, samplesperframe);
260 msg("swf_SetSoundStreamHead() done");
261 i->filesize += swf_WriteTag2(&i->out, i->tag);
262 i->soundstreamhead = 1;
265 /* for framerates greater than 19.14, every now and then a frame
266 hasn't a soundstreamblock. Determine whether this is the case.
268 msg("SOUND: frame:%d soundframepos:%f samplewritepos:%d samplepos:%f\n", i->frames, i->soundframepos, i->samplewritepos, i->samplepos);
269 if(i->frames < i->soundframepos) {
270 msg("SOUND: block skipped\n");
271 i->samplepos += samplesperframe;
272 return;
275 seek = i->seek;
277 //while(i->samplewritepos + num * blocksize < i->samplepos + blocksize) {
278 do {
279 i->samplewritepos += blocksize;
280 i->soundframepos += framesperblock;
281 num++;
283 while(i->samplewritepos < i->samplepos);
285 msg("SOUND: number of blocks: %d", num);
287 /* write num frames, max 1 block */
288 for(pos=0;pos<num;pos++) {
289 if(!getSamples(i->video, block1, blocksize * (double)swf_mp3_in_samplerate/swf_mp3_out_samplerate, speedup)) {
290 i->audio_eof = 1; i->video->samplerate = i->video->channels = 0; //end of soundtrack
291 /* fall through, this probably was a partial read. (We did, after all,
292 come to this point, so i->audio_eof must have been false so far) */
294 if(!pos) {
295 swf_ResetTag(i->tag, ST_SOUNDSTREAMBLOCK);
296 swf_SetSoundStreamBlock(i->tag, block1, seek, num);
297 } else {
298 swf_SetSoundStreamBlock(i->tag, block1, seek, 0);
301 i->filesize += swf_WriteTag2(&i->out, i->tag);
303 i->seek = blocksize - (i->samplewritepos - i->samplepos);
304 i->samplepos += samplesperframe;
307 static void writeShowFrame(v2swf_internal_t* i)
309 do {
310 writeAudioForOneFrame(i);
312 swf_ResetTag(i->tag, ST_SHOWFRAME);
313 i->filesize += swf_WriteTag2(&i->out, i->tag);
315 i->fpspos -= 1.0;
316 i->frames ++;
318 while(i->fpspos >= 1.0);
319 i->showframe = 0;
322 static void writeShowTags(v2swf_internal_t* i, int shapeid, int bmid, int width, int height)
324 writeShape(i, shapeid, bmid, width, height);
326 swf_ResetTag(i->tag, ST_PLACEOBJECT2);
327 if(!i->prescale) {
328 MATRIX m;
329 swf_GetMatrix(0, &m);
330 m.sx = m.sy = i->scale;
331 swf_ObjectPlace(i->tag,shapeid,shapeid,&m,0,0);
332 } else {
333 swf_ObjectPlace(i->tag,shapeid,shapeid,0,0,0);
335 i->filesize += swf_WriteTag2(&i->out, i->tag);
337 i->showframe = 1;
340 static int wwrite(writer_t*w, void*data, int len)
342 v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
343 ringbuffer_put(&i->r, data, len);
344 return len;
347 static void wfinish(writer_t*w)
349 v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
352 static void writehead(v2swf_internal_t*i)
354 char header[]="FWS\6\0\0\0\4";
355 SWF swf;
356 int ret;
357 int id;
359 header[3] = i->version;
360 if(i->version >= 6) { //MX
361 header[0] = 'C';
363 i->out2.write = wwrite;
364 i->out2.finish = wfinish;
365 i->out2.internal = i;
366 i->out2.type = 77;
367 i->out2.bitpos = 0;
368 i->out2.mybyte = 0;
369 i->out2.pos = 0;
370 writer_init_zlibdeflate(&i->out, &i->out2);
371 } else {
372 i->out.write = wwrite;
373 i->out.finish = wfinish;
374 i->out.internal = i;
375 i->out.type = 77;
376 i->out.bitpos = 0;
377 i->out.mybyte = 0;
378 i->out.pos = 0;
379 i->out2 = i->out;
382 if(i->prescale) {
383 i->width = (int)(i->video->width*(i->scale/65536.0));
384 i->height = (int)(i->video->height*(i->scale/65536.0));
385 } else {
386 i->width = i->video->width;
387 i->height = i->video->height;
389 if(!i->width)
390 i->width = 1;
391 if(!i->height)
392 i->height = 1;
393 i->buffer = (unsigned char*)malloc(i->width*i->height*4);
394 i->vrbuffer = (unsigned char*)malloc(i->video->width*i->video->height*4);
396 memset(&swf, 0, sizeof(SWF));
397 swf.fileVersion=i->version;
398 swf.fileSize = 0;
399 swf.frameCount = 65535;
400 swf.movieSize.xmax=i->width*20;
401 swf.movieSize.ymax=i->height*20;
402 swf.compressed = 8; /* 8 = compression done by caller (us) */
403 swf.frameRate = (int)(i->framerate*0x100);//25*0x100;
405 /* write the first 8 bytes to out */
406 i->out2.write(&i->out2, header, 8);
408 i->filesize += swf_WriteHeader2(&i->out, &swf);
409 i->headersize = i->filesize;
411 i->tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
412 swf_SetU8(i->tag, 0); //black
413 swf_SetU8(i->tag, 0);
414 swf_SetU8(i->tag, 0);
415 i->filesize += swf_WriteTag2(&i->out, i->tag);
418 static void finish(v2swf_internal_t*i)
420 msg("finish(): i->finished=%d\n", i->finished);
421 if(!i->finished) {
422 msg("write endtag\n", i->finished);
424 if(i->add_cut) {
425 swf_ResetTag(i->tag, ST_SHOWFRAME);
426 i->filesize += swf_WriteTag2(&i->out, i->tag);
428 swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
429 swf_SetU16(i->tag, 1); //depth
430 i->filesize += swf_WriteTag2(&i->out, i->tag);
432 swf_ResetTag(i->tag, ST_DOACTION);
433 swf_SetU16(i->tag, 0x0007);
434 i->filesize += swf_WriteTag2(&i->out, i->tag);
437 swf_ResetTag(i->tag, ST_END);
438 i->filesize += swf_WriteTag2(&i->out, i->tag);
440 i->out.finish(&i->out);
442 if(i->version>=6) {
443 swf_VideoStreamClear(&i->stream);
445 if(i->buffer) {
446 free(i->buffer);i->buffer = 0;
448 if(i->vrbuffer) {
449 free(i->vrbuffer);i->vrbuffer = 0;
451 if(i->lastbitmap) {
452 free(i->lastbitmap);i->lastbitmap = 0;
455 /* FIXME: we shouldn't be doing this. the caller should */
456 msg("call videoreader_close(%08x)\n", i->video);
457 videoreader_close(i->video);
459 i->finished = 1;
461 msg("finishing done\n");
463 static void cleanup(v2swf_internal_t*i)
465 int t;
466 for(t=i->lastid;t<i->id;t++) {
467 if(!(t&1)) {
468 swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
469 swf_SetU16(i->tag, t);
470 i->filesize += swf_WriteTag2(&i->out, i->tag);
472 swf_ResetTag(i->tag, ST_FREECHARACTER);
473 swf_SetU16(i->tag, t);
474 i->filesize += swf_WriteTag2(&i->out, i->tag);
476 i->lastid = i->id;
479 #define DIFFMODE_MAX 1
480 #define DIFFMODE_MEAN 2
481 #define DIFFMODE_EXACT 3
482 #define DIFFMODE_QMEAN 4
484 static int blockdiff_max(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
486 int x,y;
487 for(y=0;y<yl;y++) {
488 for(x=0;x<xl;x++) {
489 int rd = d1[1] - d2[1];
490 int gd = d1[2] - d2[2];
491 int bd = d1[3] - d2[3];
492 if(rd < 0) rd = -rd;
493 if(gd < 0) gd = -gd;
494 if(bd < 0) bd = -bd;
495 if(rd+gd+bd>maxdiff)
496 return 1;
498 d1+=4; d2+=4;
500 d1 += yadd; d2 += yadd;
502 return 0;
505 static int blockdiff_mean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
507 int mean = 0;
508 int x,y;
509 for(y=0;y<yl;y++) {
510 for(x=0;x<xl;x++) {
511 int rd = d1[1] - d2[1];
512 int gd = d1[2] - d2[2];
513 int bd = d1[3] - d2[3];
514 if(rd < 0) rd = -rd;
515 if(gd < 0) gd = -gd;
516 if(bd < 0) bd = -bd;
517 mean += rd+gd+bd;
519 d1+=4; d2+=4;
521 d1 += yadd; d2 += yadd;
523 if(mean/(xl*yl) > maxdiff)
524 return 1;
525 return 0;
528 static int blockdiff_qmean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
530 int mean = 0;
531 int x,y;
532 for(y=0;y<yl;y++) {
533 for(x=0;x<xl;x++) {
534 int rd = d1[1] - d2[1];
535 int gd = d1[2] - d2[2];
536 int bd = d1[3] - d2[3];
537 int q;
538 if(rd < 0) rd = -rd;
539 if(gd < 0) gd = -gd;
540 if(bd < 0) bd = -bd;
541 q = rd+gd+bd;
542 mean += q*q;
544 d1+=4; d2+=4;
546 d1 += yadd; d2 += yadd;
548 if(mean/(xl*yl) > maxdiff*maxdiff)
549 return 1;
550 return 0;
553 static int blockdiff_exact(U8*d1,U8*d2,int yadd, int xl, int yl)
555 int x,y;
556 for(y=0;y<yl;y++) {
557 for(x=0;x<xl;x++) {
558 if((*(U32*)d1^*(U32*)d2)&0xffffff00) { //bits [RGB_] of [RGBA] differ
559 return 1;
561 d1+=4; d2+=4;
563 d1 += yadd; d2 += yadd;
565 return 0;
568 /*U32 r = (*(U32*)d1^-(U32*)d2)&0xffffff00;
569 U32 g = ((r << 3) ^ r)&0x80808080;
570 if(g)
571 goto differ;*/
573 static void checkInit(v2swf_internal_t*i)
575 if(!i->head_done) {
576 writehead(i);
577 if(i->version>=6) {
578 swf_ResetTag(i->tag, ST_DEFINEVIDEOSTREAM);
579 swf_SetU16(i->tag, 99);
580 swf_SetVideoStreamDefine(i->tag, &i->stream, 65535, i->width, i->height);
581 i->filesize += swf_WriteTag2(&i->out, i->tag);
582 if(i->domotion) {
583 i->stream.do_motion = 1;
586 i->head_done = 1;
590 static void scaleimage(v2swf_internal_t*i)
592 int x,y;
593 int xv,yv;
594 int xm = (i->video->width*65536)/i->width;
595 int ym = (i->video->height*65536)/i->height;
596 msg("scaling from %dx%d to %dx%d\n",
597 i->video->width, i->video->height,
598 i->width, i->height
601 memset(i->buffer, 255, i->width*i->height*4);
602 for(y=0,yv=0;y<i->height;y++,yv+=ym) {
603 int*src = &((int*)i->vrbuffer)[(yv>>16)*i->video->width];
604 int*dest = &((int*)i->buffer)[y*i->width];
605 for(x=0,xv=0;x<i->width;x++,xv+=xm) {
606 dest[x] = src[xv>>16];
609 //memcpy(i->buffer, i->vrbuffer, i->width*i->height*4);
612 static int writeAudioOnly(v2swf_internal_t*i)
614 if(i->showframe) {
615 i->fpspos += i->fpsratio;
616 /* skip frames */
617 if(i->fpspos<1.0) {
618 return 0;
620 writeShowFrame(i);
622 i->showframe = 1;
623 return 1;
626 static int getframe(v2swf_internal_t*i)
628 if(!i->skipframes)
629 return videoreader_getimage(i->video, i->vrbuffer);
630 else {
631 int t;
632 for(t=0;t<i->skipframes;t++) {
633 int ret = videoreader_getimage(i->video, i->vrbuffer);
634 if(!ret)
635 return 0;
637 return 1;
641 static int encodeoneframe(v2swf_internal_t*i)
643 videoreader_t*video = i->video;
644 int ret;
646 checkInit(i);
648 if(i->video_eof && i->audio_eof) {
649 if(!i->finished)
650 finish(i);
651 return 0;
654 if(!i->audio_eof && i->video_eof) {
655 return writeAudioOnly(i);
658 if(!getframe(i) || (i->numframes && i->frames==i->numframes))
660 i->video_eof = 1;
661 msg("videoreader returned eof\n");
662 if(i->audio_eof || (i->numframes && i->frames==i->numframes)) {
663 finish(i);
664 return 0;
665 } else {
666 return writeAudioOnly(i);
670 msg("encoding image for frame %d\n", i->frames);
671 if(i->showframe) {
672 i->fpspos += i->fpsratio;
673 /* skip frames */
674 if(i->fpspos<1.0) {
675 return 0;
677 writeShowFrame(i);
680 scaleimage(i);
682 msg("version is %d\n", i->version);
684 if(i->version <= 4) {
686 int bmid = i->id++;
687 int shapeid = i->id++;
688 int width2 = i->width * 4;
690 if(i->id>=4) {
691 swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
692 swf_SetU16(i->tag, i->id-3);
693 i->filesize += swf_WriteTag2(&i->out, i->tag);
694 swf_ResetTag(i->tag, ST_FREECHARACTER);
695 swf_SetU16(i->tag, i->id-4);
696 i->filesize += swf_WriteTag2(&i->out, i->tag);
699 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
700 swf_SetU16(i->tag, bmid);
701 swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
702 i->filesize += swf_WriteTag2(&i->out, i->tag);
704 writeShowTags(i, shapeid, bmid, i->width, i->height);
706 } else if(i->version == 5) {
707 int width2 = i->width * 4;
708 int width8 = (i->width+7)/8;
709 int height8 = (i->height+7)/8;
711 /* the idea is here to only update those jpeg 8x8 blocks
712 which actually have changed. This means that we have to keep
713 the bitmap from the last frame for the comparison. */
715 (i->keyframe)--;
716 if(!i->lastbitmap || !i->keyframe) {
717 int t, bmid,shapeid;
718 cleanup(i);
720 if(!i->lastbitmap) {
721 msg("Creating bitmap buffer for %dx%d (%dx%d), (%dx%d)\n", i->width, i->height, width2, i->height, width8, height8);
722 i->lastbitmap = (U8*)malloc(width2*i->height);
724 memcpy(i->lastbitmap, i->buffer, width2*i->height);
726 i->keyframe = i->keyframe_interval;
728 bmid = i->id++;
729 shapeid = i->id++;
730 width2 = i->width * 4;
731 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
732 swf_SetU16(i->tag, bmid);
733 swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
734 i->filesize += swf_WriteTag2(&i->out, i->tag);
736 writeShowTags(i, shapeid, bmid, i->width, i->height);
737 return 1;
738 } else {
739 /* The following looks so ugly because it's somewhat optimized.
740 What it does is walk through all the 8x8 blocks, find those
741 which have changed too much and set all others to (R,G,B,A)=(0,0,0,0).
742 It also set's alpha to 255 in those who haven't changed, and
743 copies them to lastbitmap.
746 int x8, y8;
747 //int maxdiff = ((100 - i->quality)*256)/100;
748 int maxdiff = i->blockdiff*3;
749 for(y8=0;y8<height8;y8++)
750 for(x8=0;x8<width8;x8++) {
751 int x,y;
752 int xl=8,yl=8;
753 int yadd;
754 U8*d1,*d1b,*d2,*d2b;
755 if(x8*8+xl > i->width)
756 xl = i->width - x8*8;
757 if(y8*8+yl > i->height)
758 yl = i->height - y8*8;
759 d1 = &i->buffer[width2*y8*8+x8*8*4];
760 d1b = d1;
761 d2 = &i->lastbitmap[width2*y8*8+x8*8*4];
762 d2b = d2;
763 yadd = width2 - (xl*4);
765 if(i->diffmode == DIFFMODE_MAX) {
766 if(blockdiff_max(d1, d2, yadd, maxdiff, xl, yl))
767 goto differ;
768 } else if(i->diffmode == DIFFMODE_MEAN) {
769 if(blockdiff_mean(d1, d2, yadd, maxdiff, xl, yl))
770 goto differ;
771 } else if(i->diffmode == DIFFMODE_EXACT) {
772 if(blockdiff_exact(d1, d2, yadd, xl, yl))
773 goto differ;
774 } else if(i->diffmode == DIFFMODE_QMEAN) {
775 if(blockdiff_qmean(d1, d2, yadd, maxdiff, xl, yl))
776 goto differ;
779 for(y=0;y<yl;y++) {
780 for(x=0;x<xl;x++) {
781 *(U32*)d1b = 0;
782 d1b+=4;
784 d1b += yadd;
786 continue;
787 differ:
788 for(y=0;y<yl;y++) {
789 for(x=0;x<xl;x++) {
790 *(U32*)d2b = *(U32*)d1b;
791 d1b[0] = 255;
792 d1b+=4;d2b+=4;
794 d1b += yadd; d2b += yadd;
798 /* ok, done. Now a) data is zeroed out in regions which haven't changed
799 b) lastbitmap equals the bitmap we were called with
800 c) data's alpha value is set to 255 in regions which did change */
805 int bmid = i->id++;
806 int shapeid = i->id++;
808 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG3);
809 swf_SetU16(i->tag, bmid);
810 swf_SetJPEGBits3(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
811 i->filesize += swf_WriteTag2(&i->out, i->tag);
813 writeShowTags(i, shapeid, bmid, i->width, i->height);
815 } else {
816 int quant = 1+(30-(30*i->quality)/100);
817 SWFPLACEOBJECT obj;
819 swf_GetPlaceObject(0, &obj);
820 if(!i->prescale) {
821 obj.matrix.sx = obj.matrix.sy = i->scale;
824 if(i->stream.frame==0) {
825 obj.depth = 1;
826 obj.id = 99;
827 } else {
828 obj.move = 1;
829 obj.depth = 1;
830 obj.ratio = i->stream.frame;
833 swf_ResetTag(i->tag, ST_VIDEOFRAME);
834 swf_SetU16(i->tag, 99);
835 if(!(--i->keyframe)) {
836 msg("setting video I-frame, ratio=%d\n", i->stream.frame);
837 swf_SetVideoStreamIFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
838 i->keyframe = i->keyframe_interval;
839 } else {
840 msg("setting video P-frame, ratio=%d\n", i->stream.frame);
841 swf_SetVideoStreamPFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
843 i->filesize += swf_WriteTag2(&i->out, i->tag);
845 swf_ResetTag(i->tag, ST_PLACEOBJECT2);
846 swf_SetPlaceObject(i->tag,&obj);
847 i->filesize += swf_WriteTag2(&i->out, i->tag);
848 i->showframe = 1;
850 return 1;
853 static void init_fps(v2swf_internal_t*i)
855 int oldframerate = i->framerate;
856 i->framerate = i->video->fps / i->skipframes;
857 i->video_fps = ((int)(i->framerate*256))/256.0;
858 if(oldframerate)
859 msg("setting new framerate to %f\n", i->framerate);
862 int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
864 int ret = 0;
865 int t=0;
866 v2swf_internal_t* i;
867 msg("v2swf_init()\n");
868 memset(v2swf, 0, sizeof(v2swf_t));
869 i = (v2swf_internal_t*)malloc(sizeof(v2swf_internal_t));
870 memset(i, 0, sizeof(v2swf_internal_t));
871 v2swf->internal = i;
873 ringbuffer_init(&i->r);
875 i->skipframes = 1;
876 i->framerate = 0;
877 i->video = video;
878 init_fps(i);
880 msg("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
882 i->blockdiff = 64;
883 i->keyframe_interval = 8;
884 i->quality = 20;
885 i->scale = 65536;
886 i->add_cut = 1;
887 i->samplerate = 11025;
888 i->prescale = 0;
889 i->numframes= 0;
890 i->skipframes = 0;
891 i->head_done = 0;
892 i->diffmode = DIFFMODE_QMEAN;
893 i->audio_fix = 1.0;
894 i->fixheader = 0;
895 i->fpsratio = 1.00000000000;
896 i->fpspos = 0.0;
897 i->bitrate = 32;
898 i->version = 6;
899 i->buffer = 0;
900 i->lastbitmap = 0;
901 i->filesize = 8;
902 i->frames = 0;
903 i->id = 1;
904 i->lastid = 1;
905 i->keyframe = 1;
906 i->showframe = 0;
908 memset(&i->out, 0, sizeof(writer_t));
909 memset(&i->out2, 0, sizeof(writer_t));
911 return 0;
913 int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
915 v2swf_internal_t* i;
916 int l;
917 msg("v2swf_read(%d)\n", len);
918 i = (v2swf_internal_t*)v2swf->internal;
920 while(!i->finished && i->r.available < len) {
921 if(!encodeoneframe(i)) {
922 break;
925 msg("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
926 l = ringbuffer_read(&i->r, buffer, len);
928 return l;
930 void v2swf_close(v2swf_t*v2swf)
932 v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
933 msg("close(): i->finished=%d\n", i->finished);
935 /* needed only if aborting: */
936 finish(i);
938 msg("freeing memory\n");
939 free(v2swf->internal);
940 memset(v2swf, 0, sizeof(v2swf_t));
941 msg("close() done\n");
944 static int mp3_bitrates[] =
945 { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0};
947 void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
949 v2swf_internal_t* i;
951 msg("set parameters %s to %s\n", name, value);
953 if(!strcmp(name, "verbose")) {
954 verbose = 1;
955 msg("set parameters %s to %s\n", name, value);
956 return;
959 if(!v2swf || !v2swf->internal) {
960 printf("error- couldn't set parameter %s: not initialized yet\n", name);fflush(stdout);
961 return;
963 i = (v2swf_internal_t*)v2swf->internal;
965 if(!strcmp(name, "flash_version")) {
966 i->version = atoi(value);
967 } else if(!strcmp(name, "audiosync")) {
968 i->audio_fix = (int)(atof(value));
969 } else if(!strcmp(name, "addcut")) {
970 i->add_cut = atoi(value);
971 } else if(!strcmp(name, "scale")) {
972 i->scale = (int)(atof(value)*65536);
973 } else if(!strcmp(name, "scale65536")) {
974 i->scale = atoi(value);
975 } else if(!strcmp(name, "quality")) {
976 i->quality = atoi(value);
977 } else if(!strcmp(name, "skipframes")) {
978 i->skipframes = atoi(value);
979 init_fps(i);
980 } else if(!strcmp(name, "numframes")) {
981 i->numframes = atoi(value);
982 } else if(!strcmp(name, "motioncompensation")) {
983 i->domotion = atoi(value);
984 } else if(!strcmp(name, "prescale")) {
985 i->prescale = atoi(value);
986 } else if(!strcmp(name, "blockdiff")) {
987 i->blockdiff = atoi(value);
988 } else if(!strcmp(name, "fixheader")) {
989 i->fixheader = atoi(value);
990 } else if(!strcmp(name, "samplerate")) {
991 i->samplerate = atoi(value);
992 } else if(!strcmp(name, "framerate")) {
993 i->framerate = atof(value);
994 i->fpsratio = i->framerate / i->video_fps;
996 else if(!strcmp(name, "mp3_bitrate")) {
997 int t=0,o;
998 i->bitrate = o = atoi(value);
999 if(i->bitrate>160)
1000 i->bitrate = 160;
1001 while(mp3_bitrates[t]) {
1002 if(i->bitrate <= mp3_bitrates[t]) {
1003 i->bitrate = mp3_bitrates[t];
1004 break;
1006 t++;
1008 msg("bitrate %d requested, setting to %d", o, i->bitrate);
1010 else if(!strcmp(name, "blockdiff_mode")) {
1011 if(!strcmp(value, "max")) i->diffmode = DIFFMODE_MAX;
1012 else if(!strcmp(value, "mean")) i->diffmode = DIFFMODE_MEAN;
1013 else if(!strcmp(value, "qmean")) i->diffmode = DIFFMODE_QMEAN;
1014 else if(!strcmp(value, "exact")) i->diffmode = DIFFMODE_EXACT;
1015 else {
1016 printf("diffmode %s not recognized\n", value);
1017 printf("valid diffmodes are: %s\n", "max, mean, qmean, exact");
1020 else if(!strcmp(name, "keyframe_interval")
1021 || !strcmp(name, "keyframe")) {
1022 int k = atoi(value);if(k<=0) k=1;
1023 i->keyframe_interval = k;
1025 else {
1026 printf("Setting encoder.%s not recognized!\n", name);fflush(stdout);
1027 return;
1030 void v2swf_backpatch(v2swf_t*v2swf, char*filename)
1032 FILE* fi;
1033 unsigned char f;
1034 v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
1035 msg("v2swf_backpatch %s\n", filename);
1036 if(!i) {
1037 printf("call backpatch before close\n");fflush(stdout);
1039 fi = fopen(filename, "rb+");
1040 if(!fi) {
1041 printf("can't open %s\n", filename);
1042 exit(1);
1044 fseek(fi, 4, SEEK_SET);
1045 f = i->filesize ;fwrite(&f,1,1,fi);
1046 f = i->filesize >> 8 ;fwrite(&f,1,1,fi);
1047 f = i->filesize >> 16;fwrite(&f,1,1,fi);
1048 f = i->filesize >> 24;fwrite(&f,1,1,fi);
1049 if(i->version<6) {
1050 /* no compression- we can backpatch the frames too */
1051 fseek(fi, i->headersize-2, SEEK_SET);
1052 f = i->frames ;fwrite(&f,1,1,fi);
1053 f = i->frames >> 8 ;fwrite(&f,1,1,fi);
1055 fclose(fi);
1056 if(i->fixheader) {
1057 SWF tmp;
1058 int fi;
1059 msg("v2swf_backpatch %s - fix header\n", filename);
1060 memset(&tmp, 0, sizeof(tmp));
1061 fi = open(filename, O_RDONLY|O_BINARY);
1062 if(fi>=0) {
1063 if(swf_ReadSWF(fi, &tmp)>=0) {
1064 close(fi);
1065 fi = open(filename, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
1066 if(fi>=0) {
1067 swf_WriteSWF(fi, &tmp);
1068 close(fi);
1069 msg("v2swf_backpatch %s - fix header: success\n", filename);
1076 void v2swf_setvideoparameter(videoreader_t*v, char*name, char*value)
1078 msg("v2swf_setvideoparameter()");
1079 videoreader_setparameter(v, name, value);