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
46 unsigned char* vrbuffer;
47 unsigned char* buffer;
48 unsigned char* lastbitmap;
55 int keyframe_interval;
91 static int verbose = 0;
92 static int filelog = 0;
94 static void msg(char*format, ...)
101 va_start(arglist, format);
102 vsprintf(buf, format, arglist);
105 while(l && buf[l-1]=='\n') {
111 FILE*fi = fopen("debug.log", "ab+");
112 fprintf(fi, "(v2swf) %s\n", buf);
117 printf("(v2swf) %s\n", buf);
121 static void writeShape(v2swf_internal_t*i, int id, int gfxid, int width, int height)
129 swf_ResetTag(i->tag, ST_DEFINESHAPE);
130 swf_ShapeNew(&shape);
131 rgb.b = rgb.g = rgb.r = 0xff;
133 ls = swf_ShapeAddLineStyle(shape,20,&rgb);
134 swf_GetMatrix(NULL,&m);
138 fs = swf_ShapeAddBitmapFillStyle(shape,&m,gfxid,0);
139 swf_SetU16(i->tag,id); // ID
144 swf_SetRect(i->tag,&r);
146 swf_SetShapeStyles(i->tag,shape);
147 swf_ShapeCountBits(shape,NULL,NULL);
148 swf_SetShapeBits(i->tag,shape);
150 swf_ShapeSetAll(i->tag,shape,0,0,lines?ls:0,fs,0);
152 swf_ShapeSetLine(i->tag,shape,width*20,0);
153 swf_ShapeSetLine(i->tag,shape,0,height*20);
154 swf_ShapeSetLine(i->tag,shape,-width*20,0);
155 swf_ShapeSetLine(i->tag,shape,0,-height*20);
156 swf_ShapeSetEnd(i->tag);
157 i->filesize += swf_WriteTag2(&i->out, i->tag);
158 swf_ShapeFree(shape);
161 static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
164 double ratio = video->rate * speedup / 44100.0;
165 int rlen = (int)(len * ratio);
168 int r = /*resampled len */ rlen *
171 if(videoreader_getsamples(video, tmp, r) < r)
174 /* convert to 1 channel */
175 for(t=0;t<rlen;t++) {
178 for(s=0;s<video->channels;s++)
179 a += tmp[t*video->channels+s];
180 tmp[t] = a/video->channels;
183 /* down/up-sample to 44khz */
185 data[t] = tmp[(int)pos];
191 extern int swf_mp3_channels;
192 extern int swf_mp3_bitrate;
193 extern int swf_mp3_samplerate;
194 static void writeAudioForOneFrame(v2swf_internal_t* i)
197 double blockspersecond;
198 double framespersecond, framesperblock, samplesperframe, samplesperblock;
201 double speedup = i->audio_fix;
204 S16 block1[576*4 * 2];
206 msg("writeAudioForOneFrame()");
208 if(i->video->channels<=0 || i->video->rate<=0)
209 return; /* no sound in video */
211 blocksize = 576; /* 11khz samples per mp3 block */
212 blockspersecond = 11025.0/blocksize;
214 /* notice: for framerates greater than about 35, audio starts getting choppy. */
215 framespersecond = i->framerate;
217 framesperblock = framespersecond / blockspersecond;
218 samplesperframe = (blocksize * blockspersecond) / framespersecond; /* 11khz-samples per frame */
219 samplesperblock = samplesperframe * framesperblock;
221 msg("samplesperblock: %f", samplesperblock);
223 if(!i->soundstreamhead) {
224 /* first run - initialize */
225 swf_mp3_channels = 1;//i->video->channels;
226 swf_mp3_bitrate = i->bitrate;
227 swf_ResetTag(i->tag, ST_SOUNDSTREAMHEAD);
228 /* samplesperframe overrides the movie framerate: */
229 msg("swf_SetSoundStreamHead(): %08x %d", i->tag, samplesperframe);
230 swf_SetSoundStreamHead(i->tag, samplesperframe);
231 msg("swf_SetSoundStreamHead() done");
232 i->filesize += swf_WriteTag2(&i->out, i->tag);
233 i->soundstreamhead = 1;
236 /* for framerates greater than 19.14, every now and then a frame
237 hasn't a soundstreamblock. Determine whether this is the case.
239 msg("SOUND: frame:%d soundframepos:%f samplewritepos:%d samplepos:%f\n", i->frames, i->soundframepos, i->samplewritepos, i->samplepos);
240 if(i->frames < i->soundframepos) {
241 msg("SOUND: block skipped\n");
242 i->samplepos += samplesperframe;
248 //while(i->samplewritepos + num * blocksize < i->samplepos + blocksize) {
250 i->samplewritepos += blocksize;
251 i->soundframepos += framesperblock;
254 while(i->samplewritepos < i->samplepos);
256 msg("SOUND: number of blocks: %d", num);
258 /* write num frames, max 1 block */
259 for(pos=0;pos<num;pos++) {
260 if(!getSamples(i->video, block1, 576*4, speedup)) { /* 4 = 44100/11025 */
261 i->video->rate = i->video->channels = 0; //end of soundtrack
265 swf_ResetTag(i->tag, ST_SOUNDSTREAMBLOCK);
266 swf_SetSoundStreamBlock(i->tag, block1, seek, num);
268 swf_SetSoundStreamBlock(i->tag, block1, seek, 0);
271 i->filesize += swf_WriteTag2(&i->out, i->tag);
273 i->seek = blocksize - (i->samplewritepos - i->samplepos);
274 i->samplepos += samplesperframe;
277 static void writeShowFrame(v2swf_internal_t* i)
280 writeAudioForOneFrame(i);
282 swf_ResetTag(i->tag, ST_SHOWFRAME);
283 i->filesize += swf_WriteTag2(&i->out, i->tag);
288 while(i->fpspos >= 1.0);
292 static void writeShowTags(v2swf_internal_t* i, int shapeid, int bmid, int width, int height)
294 writeShape(i, shapeid, bmid, width, height);
296 swf_ResetTag(i->tag, ST_PLACEOBJECT2);
299 swf_GetMatrix(0, &m);
300 m.sx = m.sy = i->scale;
301 swf_ObjectPlace(i->tag,shapeid,shapeid,&m,0,0);
303 swf_ObjectPlace(i->tag,shapeid,shapeid,0,0,0);
305 i->filesize += swf_WriteTag2(&i->out, i->tag);
310 static int wwrite(struct writer_t*w, void*data, int len)
312 v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
313 ringbuffer_put(&i->r, data, len);
317 static void wfinish(struct writer_t*w)
319 v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
322 static void writehead(v2swf_internal_t*i)
324 char header[]="FWS\6\0\0\0\4";
329 header[3] = i->version;
330 if(i->version >= 6) { //MX
333 i->out2.write = wwrite;
334 i->out2.finish = wfinish;
335 i->out2.internal = i;
340 writer_init_zlibdeflate(&i->out, &i->out2);
342 i->out.write = wwrite;
343 i->out.finish = wfinish;
353 i->width = (int)(i->video->width*(i->scale/65536.0));
354 i->height = (int)(i->video->height*(i->scale/65536.0));
356 i->width = i->video->width;
357 i->height = i->video->height;
363 i->buffer = (unsigned char*)malloc(i->width*i->height*4);
364 i->vrbuffer = (unsigned char*)malloc(i->video->width*i->video->height*4);
366 memset(&swf, 0, sizeof(SWF));
367 swf.fileVersion=i->version;
369 swf.frameCount = 65535;
370 swf.movieSize.xmax=i->width*20;
371 swf.movieSize.ymax=i->height*20;
372 swf.compressed = 8; /* 8 = compression done by caller (us) */
373 swf.frameRate = (int)(i->framerate*0x100);//25*0x100;
375 /* write the first 8 bytes to out */
376 i->out2.write(&i->out2, header, 8);
378 i->filesize += swf_WriteHeader2(&i->out, &swf);
379 i->headersize = i->filesize;
381 i->tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
382 swf_SetU8(i->tag, 0); //black
383 swf_SetU8(i->tag, 0);
384 swf_SetU8(i->tag, 0);
385 i->filesize += swf_WriteTag2(&i->out, i->tag);
388 static void finish(v2swf_internal_t*i)
390 msg("finish(): i->finished=%d\n", i->finished);
392 msg("write endtag\n", i->finished);
394 swf_ResetTag(i->tag, ST_END);
395 i->filesize += swf_WriteTag2(&i->out, i->tag);
396 i->out.finish(&i->out);
399 swf_VideoStreamClear(&i->stream);
402 free(i->buffer);i->buffer = 0;
405 free(i->vrbuffer);i->vrbuffer = 0;
408 free(i->lastbitmap);i->lastbitmap = 0;
411 /* FIXME: we shouldn't be doing this. the caller should */
412 msg("call videoreader_close(%08x)\n", i->video);
413 videoreader_close(i->video);
417 msg("finishing done\n");
419 static void cleanup(v2swf_internal_t*i)
422 for(t=i->lastid;t<i->id;t++) {
424 swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
425 swf_SetU16(i->tag, t);
426 i->filesize += swf_WriteTag2(&i->out, i->tag);
428 swf_ResetTag(i->tag, ST_FREECHARACTER);
429 swf_SetU16(i->tag, t);
430 i->filesize += swf_WriteTag2(&i->out, i->tag);
435 #define DIFFMODE_MAX 1
436 #define DIFFMODE_MEAN 2
437 #define DIFFMODE_EXACT 3
438 #define DIFFMODE_QMEAN 4
440 static int blockdiff_max(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
445 int rd = d1[1] - d2[1];
446 int gd = d1[2] - d2[2];
447 int bd = d1[3] - d2[3];
456 d1 += yadd; d2 += yadd;
461 static int blockdiff_mean(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];
477 d1 += yadd; d2 += yadd;
479 if(mean/(xl*yl) > maxdiff)
484 static int blockdiff_qmean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
490 int rd = d1[1] - d2[1];
491 int gd = d1[2] - d2[2];
492 int bd = d1[3] - d2[3];
502 d1 += yadd; d2 += yadd;
504 if(mean/(xl*yl) > maxdiff*maxdiff)
509 static int blockdiff_exact(U8*d1,U8*d2,int yadd, int xl, int yl)
514 if((*(U32*)d1^*(U32*)d2)&0xffffff00) { //bits [RGB_] of [RGBA] differ
519 d1 += yadd; d2 += yadd;
524 /*U32 r = (*(U32*)d1^-(U32*)d2)&0xffffff00;
525 U32 g = ((r << 3) ^ r)&0x80808080;
529 static void checkInit(v2swf_internal_t*i)
534 swf_ResetTag(i->tag, ST_DEFINEVIDEOSTREAM);
535 swf_SetU16(i->tag, 99);
536 swf_SetVideoStreamDefine(i->tag, &i->stream, 65535, i->width, i->height);
537 i->filesize += swf_WriteTag2(&i->out, i->tag);
539 i->stream.do_motion = 1;
546 static void scaleimage(v2swf_internal_t*i)
550 int xm = (i->video->width*65536)/i->width;
551 int ym = (i->video->height*65536)/i->height;
552 memset(i->buffer, 255, i->width*i->height*4);
553 for(y=0,yv=0;y<i->height;y++,yv+=ym) {
554 int*src = &((int*)i->vrbuffer)[(yv>>16)*i->video->width];
555 int*dest = &((int*)i->buffer)[y*i->width];
556 for(x=0,xv=0;x<i->width;x++,xv+=xm) {
557 dest[x] = src[xv>>16];
560 //memcpy(i->buffer, i->vrbuffer, i->width*i->height*4);
563 static int encodeoneframe(v2swf_internal_t*i)
565 videoreader_t*video = i->video;
570 if(videoreader_eof(i->video) || !videoreader_getimage(i->video, i->vrbuffer))
572 msg("videoreader returned eof\n");
577 i->fpspos += i->fpsratio;
584 msg("encoding image for frame %d\n", i->frames);
593 msg("version is %d\n", i->version);
595 if(i->version <= 4) {
598 int shapeid = i->id++;
599 int width2 = i->width * 4;
602 swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
603 swf_SetU16(i->tag, i->id-3);
604 i->filesize += swf_WriteTag2(&i->out, i->tag);
605 swf_ResetTag(i->tag, ST_FREECHARACTER);
606 swf_SetU16(i->tag, i->id-4);
607 i->filesize += swf_WriteTag2(&i->out, i->tag);
610 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
611 swf_SetU16(i->tag, bmid);
612 swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
613 i->filesize += swf_WriteTag2(&i->out, i->tag);
615 writeShowTags(i, shapeid, bmid, i->width, i->height);
617 } else if(i->version == 5) {
618 int width2 = i->width * 4;
619 int width8 = (i->width+7)/8;
620 int height8 = (i->height+7)/8;
622 /* the idea is here to only update those jpeg 8x8 blocks
623 which actually have changed. This means that we have to keep
624 the bitmap from the last frame for the comparison. */
627 if(!i->lastbitmap || !i->keyframe) {
632 msg("Creating bitmap buffer for %dx%d (%dx%d), (%dx%d)\n", i->width, i->height, width2, i->height, width8, height8);
633 i->lastbitmap = (U8*)malloc(width2*i->height);
635 memcpy(i->lastbitmap, i->buffer, width2*i->height);
637 i->keyframe = i->keyframe_interval;
641 width2 = i->width * 4;
642 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
643 swf_SetU16(i->tag, bmid);
644 swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
645 i->filesize += swf_WriteTag2(&i->out, i->tag);
647 writeShowTags(i, shapeid, bmid, i->width, i->height);
650 /* The following looks so ugly because it's somewhat optimized.
651 What it does is walk through all the 8x8 blocks, find those
652 which have changed too much and set all others to (R,G,B,A)=(0,0,0,0).
653 It also set's alpha to 255 in those who haven't changed, and
654 copies them to lastbitmap.
658 //int maxdiff = ((100 - i->quality)*256)/100;
659 int maxdiff = i->blockdiff*3;
660 for(y8=0;y8<height8;y8++)
661 for(x8=0;x8<width8;x8++) {
666 if(x8*8+xl > i->width)
667 xl = i->width - x8*8;
668 if(y8*8+yl > i->height)
669 yl = i->height - y8*8;
670 d1 = &i->buffer[width2*y8*8+x8*8*4];
672 d2 = &i->lastbitmap[width2*y8*8+x8*8*4];
674 yadd = width2 - (xl*4);
676 if(i->diffmode == DIFFMODE_MAX) {
677 if(blockdiff_max(d1, d2, yadd, maxdiff, xl, yl))
679 } else if(i->diffmode == DIFFMODE_MEAN) {
680 if(blockdiff_mean(d1, d2, yadd, maxdiff, xl, yl))
682 } else if(i->diffmode == DIFFMODE_EXACT) {
683 if(blockdiff_exact(d1, d2, yadd, xl, yl))
685 } else if(i->diffmode == DIFFMODE_QMEAN) {
686 if(blockdiff_qmean(d1, d2, yadd, maxdiff, xl, yl))
701 *(U32*)d2b = *(U32*)d1b;
705 d1b += yadd; d2b += yadd;
709 /* ok, done. Now a) data is zeroed out in regions which haven't changed
710 b) lastbitmap equals the bitmap we were called with
711 c) data's alpha value is set to 255 in regions which did change */
717 int shapeid = i->id++;
719 swf_ResetTag(i->tag, ST_DEFINEBITSJPEG3);
720 swf_SetU16(i->tag, bmid);
721 swf_SetJPEGBits3(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
722 i->filesize += swf_WriteTag2(&i->out, i->tag);
724 writeShowTags(i, shapeid, bmid, i->width, i->height);
727 int quant = 1+(30-(30*i->quality)/100);
730 swf_GetPlaceObject(0, &obj);
732 obj.matrix.sx = obj.matrix.sy = i->scale;
735 if(i->stream.frame==0) {
741 obj.ratio = i->stream.frame;
744 swf_ResetTag(i->tag, ST_VIDEOFRAME);
745 swf_SetU16(i->tag, 99);
746 if(!(--i->keyframe)) {
747 msg("setting video I-frame, ratio=%d\n", i->stream.frame);
748 swf_SetVideoStreamIFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
749 i->keyframe = i->keyframe_interval;
751 msg("setting video P-frame, ratio=%d\n", i->stream.frame);
752 swf_SetVideoStreamPFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
754 i->filesize += swf_WriteTag2(&i->out, i->tag);
756 swf_ResetTag(i->tag, ST_PLACEOBJECT2);
757 swf_SetPlaceObject(i->tag,&obj);
758 i->filesize += swf_WriteTag2(&i->out, i->tag);
764 int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
769 msg("v2swf_init()\n");
770 memset(v2swf, 0, sizeof(v2swf_t));
771 i = (v2swf_internal_t*)malloc(sizeof(v2swf_internal_t));
772 memset(i, 0, sizeof(v2swf_internal_t));
775 ringbuffer_init(&i->r);
777 msg("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
781 i->keyframe_interval = 8;
786 i->diffmode = DIFFMODE_QMEAN;
789 i->framerate = video->fps;
790 i->fpsratio = 1.00000000;
803 memset(&i->out, 0, sizeof(struct writer_t));
804 memset(&i->out2, 0, sizeof(struct writer_t));
808 int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
812 msg("v2swf_read(%d)\n", len);
813 i = (v2swf_internal_t*)v2swf->internal;
815 while(!i->finished && i->r.available < len) {
816 if(!encodeoneframe(i)) {
820 msg("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
821 l = ringbuffer_read(&i->r, buffer, len);
825 void v2swf_close(v2swf_t*v2swf)
827 v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
828 msg("close(): i->finished=%d\n", i->finished);
830 /* needed only if aborting: */
833 msg("freeing memory\n");
834 free(v2swf->internal);
835 memset(v2swf, 0, sizeof(v2swf_t));
836 msg("close() done\n");
839 static int mp3_bitrates[] =
840 { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0};
842 void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
846 msg("set parameters %s to %s\n", name, value);
848 if(!strcmp(name, "verbose")) {
850 msg("set parameters %s to %s\n", name, value);
854 if(!v2swf || !v2swf->internal) {
855 printf("error- couldn't set parameter %s: not initialized yet\n", name);fflush(stdout);
858 i = (v2swf_internal_t*)v2swf->internal;
860 if(!strcmp(name, "flash_version")) {
861 i->version = atoi(value);
862 } else if(!strcmp(name, "audiosync")) {
863 i->audio_fix = (int)(atof(value));
864 } else if(!strcmp(name, "scale")) {
865 i->scale = (int)(atof(value)*65536);
866 } else if(!strcmp(name, "scale65536")) {
867 i->scale = atoi(value);
868 } else if(!strcmp(name, "quality")) {
869 i->quality = atoi(value);
870 } else if(!strcmp(name, "motioncompensation")) {
871 i->domotion = atoi(value);
872 } else if(!strcmp(name, "prescale")) {
873 i->prescale = atoi(value);
874 } else if(!strcmp(name, "blockdiff")) {
875 i->blockdiff = atoi(value);
876 } else if(!strcmp(name, "fixheader")) {
877 i->fixheader = atoi(value);
878 } else if(!strcmp(name, "framerate")) {
879 i->framerate = atof(value);
880 i->fpsratio = i->framerate / i->video->fps;
882 else if(!strcmp(name, "mp3_bitrate")) {
884 i->bitrate = o = atoi(value);
887 while(mp3_bitrates[t]) {
888 if(i->bitrate <= mp3_bitrates[t]) {
889 i->bitrate = mp3_bitrates[t];
894 msg("bitrate %d requested, setting to %d", o, i->bitrate);
896 else if(!strcmp(name, "blockdiff_mode")) {
897 if(!strcmp(value, "max")) i->diffmode = DIFFMODE_MAX;
898 else if(!strcmp(value, "mean")) i->diffmode = DIFFMODE_MEAN;
899 else if(!strcmp(value, "qmean")) i->diffmode = DIFFMODE_QMEAN;
900 else if(!strcmp(value, "exact")) i->diffmode = DIFFMODE_EXACT;
902 printf("diffmode %s not recognized\n", value);
903 printf("valid diffmodes are: %s\n", "max, mean, qmean, exact");
906 else if(!strcmp(name, "keyframe_interval")
907 || !strcmp(name, "keyframe")) {
908 int k = atoi(value);if(k<=0) k=1;
909 i->keyframe_interval = k;
912 printf("Setting encoder.%s not recognized!\n", name);fflush(stdout);
916 void v2swf_backpatch(v2swf_t*v2swf, char*filename)
920 v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
921 msg("v2swf_backpatch %s\n", filename);
923 printf("call backpatch before close\n");fflush(stdout);
925 fi = fopen(filename, "rb+");
927 printf("can't open %s\n", filename);
930 fseek(fi, 4, SEEK_SET);
931 f = i->filesize ;fwrite(&f,1,1,fi);
932 f = i->filesize >> 8 ;fwrite(&f,1,1,fi);
933 f = i->filesize >> 16;fwrite(&f,1,1,fi);
934 f = i->filesize >> 24;fwrite(&f,1,1,fi);
936 /* no compression- we can backpatch the frames too */
937 fseek(fi, i->headersize-2, SEEK_SET);
938 f = i->frames ;fwrite(&f,1,1,fi);
939 f = i->frames >> 8 ;fwrite(&f,1,1,fi);
945 msg("v2swf_backpatch %s - fix header\n", filename);
946 memset(&tmp, 0, sizeof(tmp));
947 fi = open(filename, O_RDONLY|O_BINARY);
949 if(swf_ReadSWF(fi, &tmp)>=0) {
951 fi = open(filename, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
953 swf_WriteSWC(fi, &tmp);
955 msg("v2swf_backpatch %s - fix header: success\n", filename);
962 float v2swf_getprogress(v2swf_t*v2swf)
966 msg("v2swf_getprogress()");
967 if(!v2swf || !v2swf->internal) {
970 i = (v2swf_internal_t*)v2swf->internal;
972 p = (float*)videoreader_getinfo(i->video, "position");
977 float f = i->frames/1500.0; /*fake*/
985 void v2swf_setvideoparameter(videoreader_t*v, char*name, char*value)
987 msg("v2swf_setvideoparameter()");
988 videoreader_setparameter(v, name, value);