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 */
24 #include "../lib/rfxswf.h"
27 typedef struct _v2swf_internal_t
49 unsigned char* vrbuffer;
50 unsigned char* buffer;
51 unsigned char* lastbitmap;
58 int keyframe_interval;
95 static int verbose = 0;
96 static int filelog = 0;
98 static void msg(char*format, ...)
105 va_start(arglist, format);
106 vsprintf(buf, format, arglist);
109 while(l && buf[l-1]=='\n') {
115 FILE*fi = fopen("debug.log", "ab+");
116 fprintf(fi, "(v2swf) %s\n", buf);
121 printf("(v2swf) %s\n", buf);
125 extern int swf_mp3_in_samplerate;
126 extern int swf_mp3_out_samplerate;
127 extern int swf_mp3_channels;
128 extern int swf_mp3_bitrate;
131 static void writeShape(v2swf_internal_t*i, int id, int gfxid, int width, int height)
139 swf_ResetTag(i->tag, ST_DEFINESHAPE);
140 swf_ShapeNew(&shape);
141 rgb.b = rgb.g = rgb.r = 0xff;
143 ls = swf_ShapeAddLineStyle(shape,20,&rgb);
144 swf_GetMatrix(NULL,&m);
148 fs = swf_ShapeAddBitmapFillStyle(shape,&m,gfxid,0);
149 swf_SetU16(i->tag,id); // ID
154 swf_SetRect(i->tag,&r);
156 swf_SetShapeStyles(i->tag,shape);
157 swf_ShapeCountBits(shape,NULL,NULL);
158 swf_SetShapeBits(i->tag,shape);
160 swf_ShapeSetAll(i->tag,shape,0,0,lines?ls:0,fs,0);
162 swf_ShapeSetLine(i->tag,shape,width*20,0);
163 swf_ShapeSetLine(i->tag,shape,0,height*20);
164 swf_ShapeSetLine(i->tag,shape,-width*20,0);
165 swf_ShapeSetLine(i->tag,shape,0,-height*20);
166 swf_ShapeSetEnd(i->tag);
167 i->filesize += swf_WriteTag2(&i->out, i->tag);
168 swf_ShapeFree(shape);
171 /* returns 0 on partial read */
172 static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
175 double ratio = (double) video->samplerate * speedup / swf_mp3_in_samplerate;
176 int rlen = (int)(len * ratio);
179 int r = /*resampled len */ rlen *
183 memset(tmp, 0, sizeof(tmp));
184 l = videoreader_getsamples(video, tmp, r);
188 msg("%d samples read", l);
190 /* convert to 1 channel */
191 for(t=0;t<rlen;t++) {
194 for(s=0;s<video->channels;s++)
195 a += tmp[t*video->channels+s];
196 tmp[t] = a/video->channels;
199 /* down/up-sample to the desired input samplerate (swf_mp3_in_samplerate) */
201 data[t] = tmp[(int)pos];
207 static void writeAudioForOneFrame(v2swf_internal_t* i)
210 double blockspersecond;
211 double framespersecond, framesperblock, samplesperframe, samplesperblock;
214 double speedup = i->audio_fix;
217 S16 block1[576*4 * 2];
219 msg("writeAudioForOneFrame()");
221 if(i->audio_eof || i->video->channels<=0 || i->video->samplerate<=0) {
223 return; /* no sound in video */
226 blocksize = (i->samplerate > 22050) ? 1152 : 576;
227 blockspersecond = ((double)i->samplerate)/blocksize;
229 /* notice: for framerates greater than about 35, audio starts getting choppy. */
230 framespersecond = i->framerate;
232 framesperblock = framespersecond / blockspersecond;
233 samplesperframe = (blocksize * blockspersecond) / framespersecond; /* 11khz-samples per frame */
234 samplesperblock = samplesperframe * framesperblock;
236 msg("samplesperblock: %f", samplesperblock);
238 if(!i->soundstreamhead) {
239 swf_mp3_out_samplerate = i->samplerate;
240 /* The pre-processing of sound samples in getSamples(..) above
241 re-samples the sound to swf_mp3_in_samplerate. It is best to
242 simply make it the original samplerate: */
243 swf_mp3_in_samplerate = i->video->samplerate;
245 /* first run - initialize */
246 swf_mp3_channels = 1;//i->video->channels;
247 swf_mp3_bitrate = i->bitrate;
248 swf_ResetTag(i->tag, ST_SOUNDSTREAMHEAD);
249 /* samplesperframe overrides the movie framerate: */
250 msg("swf_SetSoundStreamHead(): %08x %d", i->tag, samplesperframe);
251 swf_SetSoundStreamHead(i->tag, samplesperframe);
252 msg("swf_SetSoundStreamHead() done");
253 i->filesize += swf_WriteTag2(&i->out, i->tag);
254 i->soundstreamhead = 1;
257 /* for framerates greater than 19.14, every now and then a frame
258 hasn't a soundstreamblock. Determine whether this is the case.
260 msg("SOUND: frame:%d soundframepos:%f samplewritepos:%d samplepos:%f\n", i->frames, i->soundframepos, i->samplewritepos, i->samplepos);
261 if(i->frames < i->soundframepos) {
262 msg("SOUND: block skipped\n");
263 i->samplepos += samplesperframe;
269 //while(i->samplewritepos + num * blocksize < i->samplepos + blocksize) {
271 i->samplewritepos += blocksize;
272 i->soundframepos += framesperblock;
275 while(i->samplewritepos < i->samplepos);
277 msg("SOUND: number of blocks: %d", num);
279 /* write num frames, max 1 block */
280 for(pos=0;pos<num;pos++) {
281 if(!getSamples(i->video, block1, blocksize * (double)swf_mp3_in_samplerate/swf_mp3_out_samplerate, speedup)) {
282 i->audio_eof = 1; i->video->samplerate = i->video->channels = 0; //end of soundtrack
283 /* fall through, this probably was a partial read. (We did, after all,
284 come to this point, so i->audio_eof must have been false so far) */
287 swf_ResetTag(i->tag, ST_SOUNDSTREAMBLOCK);
288 swf_SetSoundStreamBlock(i->tag, block1, seek, num);
290 swf_SetSoundStreamBlock(i->tag, block1, seek, 0);
293 i->filesize += swf_WriteTag2(&i->out, i->tag);
295 i->seek = blocksize - (i->samplewritepos - i->samplepos);
296 i->samplepos += samplesperframe;
299 static void writeShowFrame(v2swf_internal_t* i)
302 writeAudioForOneFrame(i);
304 swf_ResetTag(i->tag, ST_SHOWFRAME);
305 i->filesize += swf_WriteTag2(&i->out, i->tag);
310 while(i->fpspos >= 1.0);
314 static void writeShowTags(v2swf_internal_t* i, int shapeid, int bmid, int width, int height)
316 writeShape(i, shapeid, bmid, width, height);
318 swf_ResetTag(i->tag, ST_PLACEOBJECT2);
321 swf_GetMatrix(0, &m);
322 m.sx = m.sy = i->scale;
323 swf_ObjectPlace(i->tag,shapeid,shapeid,&m,0,0);
325 swf_ObjectPlace(i->tag,shapeid,shapeid,0,0,0);
327 i->filesize += swf_WriteTag2(&i->out, i->tag);
332 static int wwrite(struct writer_t*w, void*data, int len)
334 v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
335 ringbuffer_put(&i->r, data, len);
339 static void wfinish(struct writer_t*w)
341 v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
344 static void writehead(v2swf_internal_t*i)
346 char header[]="FWS\6\0\0\0\4";
351 header[3] = i->version;
352 if(i->version >= 6) { //MX
355 i->out2.write = wwrite;
356 i->out2.finish = wfinish;
357 i->out2.internal = i;
362 writer_init_zlibdeflate(&i->out, &i->out2);
364 i->out.write = wwrite;
365 i->out.finish = wfinish;
375 i->width = (int)(i->video->width*(i->scale/65536.0));
376 i->height = (int)(i->video->height*(i->scale/65536.0));
378 i->width = i->video->width;
379 i->height = i->video->height;
385 i->buffer = (unsigned char*)malloc(i->width*i->height*4);
386 i->vrbuffer = (unsigned char*)malloc(i->video->width*i->video->height*4);
388 memset(&swf, 0, sizeof(SWF));
389 swf.fileVersion=i->version;
391 swf.frameCount = 65535;
392 swf.movieSize.xmax=i->width*20;
393 swf.movieSize.ymax=i->height*20;
394 swf.compressed = 8; /* 8 = compression done by caller (us) */
395 swf.frameRate = (int)(i->framerate*0x100);//25*0x100;
397 /* write the first 8 bytes to out */
398 i->out2.write(&i->out2, header, 8);
400 i->filesize += swf_WriteHeader2(&i->out, &swf);
401 i->headersize = i->filesize;
403 i->tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
404 swf_SetU8(i->tag, 0); //black
405 swf_SetU8(i->tag, 0);
406 swf_SetU8(i->tag, 0);
407 i->filesize += swf_WriteTag2(&i->out, i->tag);
410 static void finish(v2swf_internal_t*i)
412 msg("finish(): i->finished=%d\n", i->finished);
414 msg("write endtag\n", i->finished);
416 swf_ResetTag(i->tag, ST_END);
417 i->filesize += swf_WriteTag2(&i->out, i->tag);
418 i->out.finish(&i->out);
421 swf_VideoStreamClear(&i->stream);
424 free(i->buffer);i->buffer = 0;
427 free(i->vrbuffer);i->vrbuffer = 0;
430 free(i->lastbitmap);i->lastbitmap = 0;
433 /* FIXME: we shouldn't be doing this. the caller should */
434 msg("call videoreader_close(%08x)\n", i->video);
435 videoreader_close(i->video);
439 msg("finishing done\n");
441 static void cleanup(v2swf_internal_t*i)
444 for(t=i->lastid;t<i->id;t++) {
446 swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
447 swf_SetU16(i->tag, t);
448 i->filesize += swf_WriteTag2(&i->out, i->tag);
450 swf_ResetTag(i->tag, ST_FREECHARACTER);
451 swf_SetU16(i->tag, t);
452 i->filesize += swf_WriteTag2(&i->out, i->tag);
457 #define DIFFMODE_MAX 1
458 #define DIFFMODE_MEAN 2
459 #define DIFFMODE_EXACT 3
460 #define DIFFMODE_QMEAN 4
462 static int blockdiff_max(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
467 int rd = d1[1] - d2[1];
468 int gd = d1[2] - d2[2];
469 int bd = d1[3] - d2[3];
478 d1 += yadd; d2 += yadd;
483 static int blockdiff_mean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
489 int rd = d1[1] - d2[1];
490 int gd = d1[2] - d2[2];
491 int bd = d1[3] - d2[3];
499 d1 += yadd; d2 += yadd;
501 if(mean/(xl*yl) > maxdiff)
506 static int blockdiff_qmean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
512 int rd = d1[1] - d2[1];
513 int gd = d1[2] - d2[2];
514 int bd = d1[3] - d2[3];
524 d1 += yadd; d2 += yadd;
526 if(mean/(xl*yl) > maxdiff*maxdiff)
531 static int blockdiff_exact(U8*d1,U8*d2,int yadd, int xl, int yl)
536 if((*(U32*)d1^*(U32*)d2)&0xffffff00) { //bits [RGB_] of [RGBA] differ
541 d1 += yadd; d2 += yadd;
546 /*U32 r = (*(U32*)d1^-(U32*)d2)&0xffffff00;
547 U32 g = ((r << 3) ^ r)&0x80808080;
551 static void checkInit(v2swf_internal_t*i)
556 swf_ResetTag(i->tag, ST_DEFINEVIDEOSTREAM);
557 swf_SetU16(i->tag, 99);
558 swf_SetVideoStreamDefine(i->tag, &i->stream, 65535, i->width, i->height);
559 i->filesize += swf_WriteTag2(&i->out, i->tag);
561 i->stream.do_motion = 1;
568 static void scaleimage(v2swf_internal_t*i)
572 int xm = (i->video->width*65536)/i->width;
573 int ym = (i->video->height*65536)/i->height;
574 memset(i->buffer, 255, i->width*i->height*4);
575 for(y=0,yv=0;y<i->height;y++,yv+=ym) {
576 int*src = &((int*)i->vrbuffer)[(yv>>16)*i->video->width];
577 int*dest = &((int*)i->buffer)[y*i->width];
578 for(x=0,xv=0;x<i->width;x++,xv+=xm) {
579 dest[x] = src[xv>>16];
582 //memcpy(i->buffer, i->vrbuffer, i->width*i->height*4);
585 static int writeAudioOnly(v2swf_internal_t*i)
588 i->fpspos += i->fpsratio;
599 static int encodeoneframe(v2swf_internal_t*i)
601 videoreader_t*video = i->video;
606 if(i->video_eof && i->audio_eof) {
612 if(!i->audio_eof && i->video_eof) {
613 return writeAudioOnly(i);
616 if(!videoreader_getimage(i->video, i->vrbuffer))
619 msg("videoreader returned eof\n");
624 return writeAudioOnly(i);
628 msg("encoding image for frame %d\n", i->frames);
630 i->fpspos += i->fpsratio;
642 msg("version is %d\n", i->version);
644 if(i->version <= 4) {
647 int shapeid = i->id++;
648 int width2 = i->width * 4;
651 swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
652 swf_SetU16(i->tag, i->id-3);
653 i->filesize += swf_WriteTag2(&i->out, i->tag);
654 swf_ResetTag(i->tag, ST_FREECHARACTER);
655 swf_SetU16(i->tag, i->id-4);
656 i->filesize += swf_WriteTag2(&i->out, i->tag);
659 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
660 swf_SetU16(i->tag, bmid);
661 swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
662 i->filesize += swf_WriteTag2(&i->out, i->tag);
664 writeShowTags(i, shapeid, bmid, i->width, i->height);
666 } else if(i->version == 5) {
667 int width2 = i->width * 4;
668 int width8 = (i->width+7)/8;
669 int height8 = (i->height+7)/8;
671 /* the idea is here to only update those jpeg 8x8 blocks
672 which actually have changed. This means that we have to keep
673 the bitmap from the last frame for the comparison. */
676 if(!i->lastbitmap || !i->keyframe) {
681 msg("Creating bitmap buffer for %dx%d (%dx%d), (%dx%d)\n", i->width, i->height, width2, i->height, width8, height8);
682 i->lastbitmap = (U8*)malloc(width2*i->height);
684 memcpy(i->lastbitmap, i->buffer, width2*i->height);
686 i->keyframe = i->keyframe_interval;
690 width2 = i->width * 4;
691 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
692 swf_SetU16(i->tag, bmid);
693 swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
694 i->filesize += swf_WriteTag2(&i->out, i->tag);
696 writeShowTags(i, shapeid, bmid, i->width, i->height);
699 /* The following looks so ugly because it's somewhat optimized.
700 What it does is walk through all the 8x8 blocks, find those
701 which have changed too much and set all others to (R,G,B,A)=(0,0,0,0).
702 It also set's alpha to 255 in those who haven't changed, and
703 copies them to lastbitmap.
707 //int maxdiff = ((100 - i->quality)*256)/100;
708 int maxdiff = i->blockdiff*3;
709 for(y8=0;y8<height8;y8++)
710 for(x8=0;x8<width8;x8++) {
715 if(x8*8+xl > i->width)
716 xl = i->width - x8*8;
717 if(y8*8+yl > i->height)
718 yl = i->height - y8*8;
719 d1 = &i->buffer[width2*y8*8+x8*8*4];
721 d2 = &i->lastbitmap[width2*y8*8+x8*8*4];
723 yadd = width2 - (xl*4);
725 if(i->diffmode == DIFFMODE_MAX) {
726 if(blockdiff_max(d1, d2, yadd, maxdiff, xl, yl))
728 } else if(i->diffmode == DIFFMODE_MEAN) {
729 if(blockdiff_mean(d1, d2, yadd, maxdiff, xl, yl))
731 } else if(i->diffmode == DIFFMODE_EXACT) {
732 if(blockdiff_exact(d1, d2, yadd, xl, yl))
734 } else if(i->diffmode == DIFFMODE_QMEAN) {
735 if(blockdiff_qmean(d1, d2, yadd, maxdiff, xl, yl))
750 *(U32*)d2b = *(U32*)d1b;
754 d1b += yadd; d2b += yadd;
758 /* ok, done. Now a) data is zeroed out in regions which haven't changed
759 b) lastbitmap equals the bitmap we were called with
760 c) data's alpha value is set to 255 in regions which did change */
766 int shapeid = i->id++;
768 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG3);
769 swf_SetU16(i->tag, bmid);
770 swf_SetJPEGBits3(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
771 i->filesize += swf_WriteTag2(&i->out, i->tag);
773 writeShowTags(i, shapeid, bmid, i->width, i->height);
776 int quant = 1+(30-(30*i->quality)/100);
779 swf_GetPlaceObject(0, &obj);
781 obj.matrix.sx = obj.matrix.sy = i->scale;
784 if(i->stream.frame==0) {
790 obj.ratio = i->stream.frame;
793 swf_ResetTag(i->tag, ST_VIDEOFRAME);
794 swf_SetU16(i->tag, 99);
795 if(!(--i->keyframe)) {
796 msg("setting video I-frame, ratio=%d\n", i->stream.frame);
797 swf_SetVideoStreamIFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
798 i->keyframe = i->keyframe_interval;
800 msg("setting video P-frame, ratio=%d\n", i->stream.frame);
801 swf_SetVideoStreamPFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
803 i->filesize += swf_WriteTag2(&i->out, i->tag);
805 swf_ResetTag(i->tag, ST_PLACEOBJECT2);
806 swf_SetPlaceObject(i->tag,&obj);
807 i->filesize += swf_WriteTag2(&i->out, i->tag);
813 int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
818 msg("v2swf_init()\n");
819 memset(v2swf, 0, sizeof(v2swf_t));
820 i = (v2swf_internal_t*)malloc(sizeof(v2swf_internal_t));
821 memset(i, 0, sizeof(v2swf_internal_t));
824 ringbuffer_init(&i->r);
826 msg("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
830 i->keyframe_interval = 8;
833 i->samplerate = 11025;
836 i->diffmode = DIFFMODE_QMEAN;
839 i->framerate = video->fps;
840 i->fpsratio = 1.00000000000;
853 memset(&i->out, 0, sizeof(struct writer_t));
854 memset(&i->out2, 0, sizeof(struct writer_t));
858 int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
862 msg("v2swf_read(%d)\n", len);
863 i = (v2swf_internal_t*)v2swf->internal;
865 while(!i->finished && i->r.available < len) {
866 if(!encodeoneframe(i)) {
870 msg("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
871 l = ringbuffer_read(&i->r, buffer, len);
875 void v2swf_close(v2swf_t*v2swf)
877 v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
878 msg("close(): i->finished=%d\n", i->finished);
880 /* needed only if aborting: */
883 msg("freeing memory\n");
884 free(v2swf->internal);
885 memset(v2swf, 0, sizeof(v2swf_t));
886 msg("close() done\n");
889 static int mp3_bitrates[] =
890 { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0};
892 void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
896 msg("set parameters %s to %s\n", name, value);
898 if(!strcmp(name, "verbose")) {
900 msg("set parameters %s to %s\n", name, value);
904 if(!v2swf || !v2swf->internal) {
905 printf("error- couldn't set parameter %s: not initialized yet\n", name);fflush(stdout);
908 i = (v2swf_internal_t*)v2swf->internal;
910 if(!strcmp(name, "flash_version")) {
911 i->version = atoi(value);
912 } else if(!strcmp(name, "audiosync")) {
913 i->audio_fix = (int)(atof(value));
914 } else if(!strcmp(name, "scale")) {
915 i->scale = (int)(atof(value)*65536);
916 } else if(!strcmp(name, "scale65536")) {
917 i->scale = atoi(value);
918 } else if(!strcmp(name, "quality")) {
919 i->quality = atoi(value);
920 } else if(!strcmp(name, "motioncompensation")) {
921 i->domotion = atoi(value);
922 } else if(!strcmp(name, "prescale")) {
923 i->prescale = atoi(value);
924 } else if(!strcmp(name, "blockdiff")) {
925 i->blockdiff = atoi(value);
926 } else if(!strcmp(name, "fixheader")) {
927 i->fixheader = atoi(value);
928 } else if(!strcmp(name, "samplerate")) {
929 i->samplerate = atoi(value);
930 } else if(!strcmp(name, "framerate")) {
931 i->framerate = atof(value);
932 i->fpsratio = i->framerate / i->video->fps;
934 else if(!strcmp(name, "mp3_bitrate")) {
936 i->bitrate = o = atoi(value);
939 while(mp3_bitrates[t]) {
940 if(i->bitrate <= mp3_bitrates[t]) {
941 i->bitrate = mp3_bitrates[t];
946 msg("bitrate %d requested, setting to %d", o, i->bitrate);
948 else if(!strcmp(name, "blockdiff_mode")) {
949 if(!strcmp(value, "max")) i->diffmode = DIFFMODE_MAX;
950 else if(!strcmp(value, "mean")) i->diffmode = DIFFMODE_MEAN;
951 else if(!strcmp(value, "qmean")) i->diffmode = DIFFMODE_QMEAN;
952 else if(!strcmp(value, "exact")) i->diffmode = DIFFMODE_EXACT;
954 printf("diffmode %s not recognized\n", value);
955 printf("valid diffmodes are: %s\n", "max, mean, qmean, exact");
958 else if(!strcmp(name, "keyframe_interval")
959 || !strcmp(name, "keyframe")) {
960 int k = atoi(value);if(k<=0) k=1;
961 i->keyframe_interval = k;
964 printf("Setting encoder.%s not recognized!\n", name);fflush(stdout);
968 void v2swf_backpatch(v2swf_t*v2swf, char*filename)
972 v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
973 msg("v2swf_backpatch %s\n", filename);
975 printf("call backpatch before close\n");fflush(stdout);
977 fi = fopen(filename, "rb+");
979 printf("can't open %s\n", filename);
982 fseek(fi, 4, SEEK_SET);
983 f = i->filesize ;fwrite(&f,1,1,fi);
984 f = i->filesize >> 8 ;fwrite(&f,1,1,fi);
985 f = i->filesize >> 16;fwrite(&f,1,1,fi);
986 f = i->filesize >> 24;fwrite(&f,1,1,fi);
988 /* no compression- we can backpatch the frames too */
989 fseek(fi, i->headersize-2, SEEK_SET);
990 f = i->frames ;fwrite(&f,1,1,fi);
991 f = i->frames >> 8 ;fwrite(&f,1,1,fi);
997 msg("v2swf_backpatch %s - fix header\n", filename);
998 memset(&tmp, 0, sizeof(tmp));
999 fi = open(filename, O_RDONLY|O_BINARY);
1001 if(swf_ReadSWF(fi, &tmp)>=0) {
1003 fi = open(filename, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
1005 swf_WriteSWC(fi, &tmp);
1007 msg("v2swf_backpatch %s - fix header: success\n", filename);
1014 void v2swf_setvideoparameter(videoreader_t*v, char*name, char*value)
1016 msg("v2swf_setvideoparameter()");
1017 videoreader_setparameter(v, name, value);