swfmill 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/.swfmill +240 -0
- data/LICENSE +340 -0
- data/README.rdoc +37 -0
- data/Rakefile +59 -0
- data/VERSION +1 -0
- data/ext/.gitignore +5 -0
- data/ext/extconf.rb +86 -0
- data/ext/swfmill/.gitignore +27 -0
- data/ext/swfmill/AUTHORS +9 -0
- data/ext/swfmill/COPYING +340 -0
- data/ext/swfmill/Makefile.am +14 -0
- data/ext/swfmill/Makefile.in +635 -0
- data/ext/swfmill/NEWS +189 -0
- data/ext/swfmill/README +170 -0
- data/ext/swfmill/TODO +33 -0
- data/ext/swfmill/aclocal.m4 +7712 -0
- data/ext/swfmill/autogen.sh +7 -0
- data/ext/swfmill/compile +142 -0
- data/ext/swfmill/config.guess +1516 -0
- data/ext/swfmill/config.sub +1626 -0
- data/ext/swfmill/configure +21868 -0
- data/ext/swfmill/configure.ac +55 -0
- data/ext/swfmill/depcomp +589 -0
- data/ext/swfmill/install-sh +519 -0
- data/ext/swfmill/ltmain.sh +6964 -0
- data/ext/swfmill/missing +367 -0
- data/ext/swfmill/src/Geom.cpp +107 -0
- data/ext/swfmill/src/Geom.h +100 -0
- data/ext/swfmill/src/Makefile.am +86 -0
- data/ext/swfmill/src/Makefile.in +1025 -0
- data/ext/swfmill/src/SWF.h +11941 -0
- data/ext/swfmill/src/SWFAction.cpp +41 -0
- data/ext/swfmill/src/SWFAction.h +19 -0
- data/ext/swfmill/src/SWFBasic.h +7 -0
- data/ext/swfmill/src/SWFFile.cpp +406 -0
- data/ext/swfmill/src/SWFFile.h +34 -0
- data/ext/swfmill/src/SWFFilter.cpp +25 -0
- data/ext/swfmill/src/SWFFilter.h +15 -0
- data/ext/swfmill/src/SWFGlyphList.cpp +262 -0
- data/ext/swfmill/src/SWFGlyphList.h +34 -0
- data/ext/swfmill/src/SWFIdItem.h +85 -0
- data/ext/swfmill/src/SWFIdItems.h +16 -0
- data/ext/swfmill/src/SWFItem.cpp +235 -0
- data/ext/swfmill/src/SWFItem.h +60 -0
- data/ext/swfmill/src/SWFList.h +179 -0
- data/ext/swfmill/src/SWFReader.cpp +352 -0
- data/ext/swfmill/src/SWFReader.h +73 -0
- data/ext/swfmill/src/SWFShapeItem.cpp +220 -0
- data/ext/swfmill/src/SWFShapeItem.h +45 -0
- data/ext/swfmill/src/SWFShapeMaker.cpp +401 -0
- data/ext/swfmill/src/SWFShapeMaker.h +128 -0
- data/ext/swfmill/src/SWFTag.cpp +49 -0
- data/ext/swfmill/src/SWFTag.h +20 -0
- data/ext/swfmill/src/SWFTrait.cpp +35 -0
- data/ext/swfmill/src/SWFTrait.h +22 -0
- data/ext/swfmill/src/SWFWriter.cpp +312 -0
- data/ext/swfmill/src/SWFWriter.h +84 -0
- data/ext/swfmill/src/base64.c +110 -0
- data/ext/swfmill/src/base64.h +15 -0
- data/ext/swfmill/src/codegen/Makefile.am +15 -0
- data/ext/swfmill/src/codegen/Makefile.in +346 -0
- data/ext/swfmill/src/codegen/basic.xsl +45 -0
- data/ext/swfmill/src/codegen/basics.xsl +235 -0
- data/ext/swfmill/src/codegen/dumper.xsl +109 -0
- data/ext/swfmill/src/codegen/header.xsl +131 -0
- data/ext/swfmill/src/codegen/mk.xsl +26 -0
- data/ext/swfmill/src/codegen/parser.xsl +196 -0
- data/ext/swfmill/src/codegen/parsexml.xsl +312 -0
- data/ext/swfmill/src/codegen/size.xsl +189 -0
- data/ext/swfmill/src/codegen/source.xml +2197 -0
- data/ext/swfmill/src/codegen/writer.xsl +190 -0
- data/ext/swfmill/src/codegen/writexml.xsl +138 -0
- data/ext/swfmill/src/swfmill.cpp +482 -0
- data/ext/swfmill/src/swft/Makefile.am +55 -0
- data/ext/swfmill/src/swft/Makefile.in +717 -0
- data/ext/swfmill/src/swft/Parser.cpp +76 -0
- data/ext/swfmill/src/swft/Parser.h +37 -0
- data/ext/swfmill/src/swft/SVGAttributeParser.cpp +78 -0
- data/ext/swfmill/src/swft/SVGAttributeParser.h +35 -0
- data/ext/swfmill/src/swft/SVGColor.cpp +116 -0
- data/ext/swfmill/src/swft/SVGColor.h +37 -0
- data/ext/swfmill/src/swft/SVGColors.h +167 -0
- data/ext/swfmill/src/swft/SVGGradient.cpp +258 -0
- data/ext/swfmill/src/swft/SVGGradient.h +81 -0
- data/ext/swfmill/src/swft/SVGPathParser.cpp +155 -0
- data/ext/swfmill/src/swft/SVGPathParser.h +126 -0
- data/ext/swfmill/src/swft/SVGPointsParser.cpp +51 -0
- data/ext/swfmill/src/swft/SVGPointsParser.h +25 -0
- data/ext/swfmill/src/swft/SVGStyle.cpp +181 -0
- data/ext/swfmill/src/swft/SVGStyle.h +80 -0
- data/ext/swfmill/src/swft/SVGTransformParser.cpp +72 -0
- data/ext/swfmill/src/swft/SVGTransformParser.h +32 -0
- data/ext/swfmill/src/swft/readpng.c +305 -0
- data/ext/swfmill/src/swft/readpng.h +92 -0
- data/ext/swfmill/src/swft/swft.cpp +251 -0
- data/ext/swfmill/src/swft/swft.h +64 -0
- data/ext/swfmill/src/swft/swft_document.cpp +57 -0
- data/ext/swfmill/src/swft/swft_import.cpp +38 -0
- data/ext/swfmill/src/swft/swft_import_binary.cpp +82 -0
- data/ext/swfmill/src/swft/swft_import_jpeg.cpp +255 -0
- data/ext/swfmill/src/swft/swft_import_mp3.cpp +268 -0
- data/ext/swfmill/src/swft/swft_import_png.cpp +231 -0
- data/ext/swfmill/src/swft/swft_import_ttf.cpp +519 -0
- data/ext/swfmill/src/swft/swft_import_wav.cpp +255 -0
- data/ext/swfmill/src/swft/swft_path.cpp +178 -0
- data/ext/swfmill/src/xslt/Makefile.am +51 -0
- data/ext/swfmill/src/xslt/Makefile.in +536 -0
- data/ext/swfmill/src/xslt/README +19 -0
- data/ext/swfmill/src/xslt/assemble.xsl +38 -0
- data/ext/swfmill/src/xslt/simple-deprecated.xslt +62 -0
- data/ext/swfmill/src/xslt/simple-elements.xslt +627 -0
- data/ext/swfmill/src/xslt/simple-import.xslt +565 -0
- data/ext/swfmill/src/xslt/simple-svg.xslt +383 -0
- data/ext/swfmill/src/xslt/simple-tools.xslt +255 -0
- data/ext/swfmill/src/xslt/simple.cpp +1686 -0
- data/ext/swfmill/src/xslt/simple.xml +7 -0
- data/ext/swfmill/src/xslt/xslt.h +7 -0
- data/ext/swfmill/test/Makefile.am +1 -0
- data/ext/swfmill/test/Makefile.in +490 -0
- data/ext/swfmill/test/xml/Makefile.am +20 -0
- data/ext/swfmill/test/xml/Makefile.in +353 -0
- data/ext/swfmill/test/xml/test-xml +21 -0
- data/ext/swfmill_ext.cc +375 -0
- data/lib/swfmill.rb +30 -0
- data/spec/data/swfmill-banner1.swf +0 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/swfmill_spec.rb +125 -0
- metadata +206 -0
@@ -0,0 +1,255 @@
|
|
1
|
+
#include <libxslt/extensions.h>
|
2
|
+
#include <libxslt/xsltutils.h>
|
3
|
+
#include <libxslt/variables.h>
|
4
|
+
#include <libxml/xpathInternals.h>
|
5
|
+
#include "swft.h"
|
6
|
+
#include <sys/types.h>
|
7
|
+
#include <sys/stat.h>
|
8
|
+
#include <cstring>
|
9
|
+
#include "readpng.h"
|
10
|
+
#include <zlib.h>
|
11
|
+
|
12
|
+
#define TMP_STRLEN 0xff
|
13
|
+
const char jpeg_header[] = { 0xff, 0xd9, 0xff, 0xd8, 0 };
|
14
|
+
|
15
|
+
// defined in swft_import_png
|
16
|
+
bool compress( unsigned char *inputBuffer, int inLength, unsigned char *outputBuffer, int *outLength );
|
17
|
+
|
18
|
+
|
19
|
+
int getJpegWord( FILE *fp ) {
|
20
|
+
int r = fgetc(fp);
|
21
|
+
r <<= 8;
|
22
|
+
r += fgetc(fp);
|
23
|
+
return r;
|
24
|
+
}
|
25
|
+
|
26
|
+
void swft_import_jpeg( xmlXPathParserContextPtr ctx, int nargs ) {
|
27
|
+
xsltTransformContextPtr tctx;
|
28
|
+
unsigned char *filename;
|
29
|
+
xsltDocumentPtr xsltdoc;
|
30
|
+
xmlDocPtr doc = NULL;
|
31
|
+
xmlNodePtr node;
|
32
|
+
xmlXPathObjectPtr obj;
|
33
|
+
char tmp[TMP_STRLEN];
|
34
|
+
|
35
|
+
xmlXPathStringFunction(ctx, 1);
|
36
|
+
if (ctx->value->type != XPATH_STRING) {
|
37
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
38
|
+
"swft:import-jpeg() : invalid arg expecting a string\n");
|
39
|
+
ctx->error = XPATH_INVALID_TYPE;
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
obj = valuePop(ctx);
|
43
|
+
if (obj->stringval == NULL) {
|
44
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
45
|
+
return;
|
46
|
+
}
|
47
|
+
|
48
|
+
tctx = xsltXPathGetTransformContext(ctx);
|
49
|
+
filename = swft_get_filename( obj->stringval );
|
50
|
+
|
51
|
+
bool quiet = true;
|
52
|
+
xmlXPathObjectPtr quietObj = xsltVariableLookup( tctx, (const xmlChar*)"quiet", NULL );
|
53
|
+
if( quietObj && quietObj->stringval ) { quiet = !strcmp("true",(const char*)quietObj->stringval ); };
|
54
|
+
|
55
|
+
|
56
|
+
FILE *fp = fopen( (const char *)filename, "rb" );
|
57
|
+
if( !fp ) {
|
58
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
59
|
+
"swft:import-jpeg() : failed to read file '%s'\n", (const char *)filename);
|
60
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
|
64
|
+
doc = xmlNewDoc( (const xmlChar *)"1.0");
|
65
|
+
doc->xmlRootNode = xmlNewDocNode( doc, NULL, (const xmlChar *)"jpeg", NULL );
|
66
|
+
node = doc->xmlRootNode;
|
67
|
+
|
68
|
+
swft_addFileName( node, (const char *)filename );
|
69
|
+
|
70
|
+
// figure width/height
|
71
|
+
int width=-1, height=-1;
|
72
|
+
while( !feof( fp ) ) { // could do a && width==-1 here, but that captures preview imgs...
|
73
|
+
if( fgetc(fp) == 0xff ) {
|
74
|
+
if( fgetc(fp) == 0xc0 ) {
|
75
|
+
// StartOfFrame
|
76
|
+
// skip length and precision (UGLY, eh?)
|
77
|
+
fgetc(fp); fgetc(fp); fgetc(fp);
|
78
|
+
|
79
|
+
// read height, width
|
80
|
+
height = getJpegWord( fp );
|
81
|
+
width = getJpegWord( fp );
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
snprintf(tmp,TMP_STRLEN,"%i", width);
|
86
|
+
xmlSetProp( node, (const xmlChar *)"width", (const xmlChar *)&tmp );
|
87
|
+
snprintf(tmp,TMP_STRLEN,"%i", height);
|
88
|
+
xmlSetProp( node, (const xmlChar *)"height", (const xmlChar *)&tmp );
|
89
|
+
|
90
|
+
// add data
|
91
|
+
unsigned char *data = NULL;
|
92
|
+
int size, ofs;
|
93
|
+
struct stat filestat;
|
94
|
+
if( stat( (const char *)filename, &filestat ) ) goto fail;
|
95
|
+
size = filestat.st_size;
|
96
|
+
|
97
|
+
ofs = strlen(jpeg_header);
|
98
|
+
|
99
|
+
rewind(fp);
|
100
|
+
data = new unsigned char[size+ofs];
|
101
|
+
memcpy( data, jpeg_header, ofs );
|
102
|
+
if( fread( &data[ofs], 1, size, fp ) != size ) {
|
103
|
+
fprintf(stderr,"WARNING: could not read enough (%i) bytes for jpeg %s\n", size, filename );
|
104
|
+
goto fail;
|
105
|
+
}
|
106
|
+
|
107
|
+
if( !quiet ) {
|
108
|
+
fprintf(stderr,"Importing JPG: '%s'\n",filename);
|
109
|
+
}
|
110
|
+
|
111
|
+
swft_addData( node, (char*)data, size+ofs );
|
112
|
+
valuePush( ctx, xmlXPathNewNodeSet( (xmlNodePtr)doc ) );
|
113
|
+
|
114
|
+
fail:
|
115
|
+
if( fp ) fclose(fp);
|
116
|
+
if( data ) delete data;
|
117
|
+
}
|
118
|
+
|
119
|
+
|
120
|
+
void swft_import_jpega( xmlXPathParserContextPtr ctx, int nargs ) {
|
121
|
+
xsltTransformContextPtr tctx;
|
122
|
+
xmlChar *filename, *maskfilename;
|
123
|
+
xsltDocumentPtr xsltdoc;
|
124
|
+
xmlDocPtr doc = NULL;
|
125
|
+
xmlNodePtr node;
|
126
|
+
xmlXPathObjectPtr obj;
|
127
|
+
char tmp[TMP_STRLEN];
|
128
|
+
|
129
|
+
if( (nargs != 2) ) {
|
130
|
+
xmlXPathSetArityError(ctx);
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
|
134
|
+
maskfilename = swft_get_filename( xmlXPathPopString(ctx) );
|
135
|
+
filename = swft_get_filename( xmlXPathPopString(ctx) );
|
136
|
+
if( xmlXPathCheckError(ctx) || (filename==NULL) || (maskfilename==NULL) ) {
|
137
|
+
return;
|
138
|
+
}
|
139
|
+
|
140
|
+
tctx = xsltXPathGetTransformContext(ctx);
|
141
|
+
|
142
|
+
FILE *fp = fopen( (const char *)filename, "rb" );
|
143
|
+
if( !fp ) {
|
144
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
145
|
+
"swft:import-jpega() : failed to read file '%s'\n", (const char *)filename);
|
146
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
147
|
+
return;
|
148
|
+
}
|
149
|
+
|
150
|
+
doc = xmlNewDoc( (const xmlChar *)"1.0");
|
151
|
+
doc->xmlRootNode = xmlNewDocNode( doc, NULL, (const xmlChar *)"jpega", NULL );
|
152
|
+
node = doc->xmlRootNode;
|
153
|
+
|
154
|
+
swft_addFileName( node, (const char *)filename );
|
155
|
+
|
156
|
+
// figure width/height
|
157
|
+
int width=-1, height=-1;
|
158
|
+
while( !feof( fp ) ) { // could do a && width==-1 here, but that captures preview imgs...
|
159
|
+
if( fgetc(fp) == 0xff ) {
|
160
|
+
if( fgetc(fp) == 0xc0 ) {
|
161
|
+
// StartOfFrame
|
162
|
+
// skip length and precision (UGLY, eh?)
|
163
|
+
fgetc(fp); fgetc(fp); fgetc(fp);
|
164
|
+
|
165
|
+
// read height, width
|
166
|
+
height = getJpegWord( fp );
|
167
|
+
width = getJpegWord( fp );
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
snprintf(tmp,TMP_STRLEN,"%i", width);
|
172
|
+
xmlSetProp( node, (const xmlChar *)"width", (const xmlChar *)&tmp );
|
173
|
+
snprintf(tmp,TMP_STRLEN,"%i", height);
|
174
|
+
xmlSetProp( node, (const xmlChar *)"height", (const xmlChar *)&tmp );
|
175
|
+
|
176
|
+
// add data
|
177
|
+
unsigned char *jpegdata = NULL;
|
178
|
+
unsigned char *data = NULL;
|
179
|
+
int data_size, mask_size;
|
180
|
+
int size, ofs;
|
181
|
+
struct stat filestat;
|
182
|
+
unsigned char *maskdata;
|
183
|
+
unsigned long maskw, maskh, rowbytes;
|
184
|
+
int channels;
|
185
|
+
png_colorp palette;
|
186
|
+
int n_pal;
|
187
|
+
unsigned char *mask;
|
188
|
+
if( stat( (const char *)filename, &filestat ) ) goto fail;
|
189
|
+
size = filestat.st_size;
|
190
|
+
|
191
|
+
ofs = strlen(jpeg_header);
|
192
|
+
|
193
|
+
rewind(fp);
|
194
|
+
jpegdata = new unsigned char[size+ofs];
|
195
|
+
memcpy( jpegdata, jpeg_header, ofs );
|
196
|
+
if( fread( &jpegdata[ofs], 1, size, fp ) != size ) {
|
197
|
+
fprintf(stderr,"WARNING: could not read enough (%i) bytes for jpeg %s\n", size, filename );
|
198
|
+
goto fail;
|
199
|
+
}
|
200
|
+
|
201
|
+
snprintf(tmp,TMP_STRLEN,"%i", size+ofs);
|
202
|
+
xmlSetProp( node, (const xmlChar *)"offset_to_alpha", (const xmlChar *)&tmp );
|
203
|
+
|
204
|
+
fclose(fp);
|
205
|
+
|
206
|
+
// jpegdata is filled, now read the mask png.
|
207
|
+
fp = fopen( (const char *)maskfilename, "rb" );
|
208
|
+
if( !fp ) {
|
209
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
210
|
+
"swft:import-jpega() : failed to read mask file '%s'\n", (const char *)maskfilename);
|
211
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
212
|
+
goto fail;
|
213
|
+
}
|
214
|
+
|
215
|
+
data_size = size + ofs + (width*height);
|
216
|
+
data = new unsigned char[ data_size ];
|
217
|
+
memcpy( data, jpegdata, size+ofs );
|
218
|
+
maskdata = &data[ size+ofs ];
|
219
|
+
|
220
|
+
if( readpng_init( fp, &maskw, &maskh ) ) goto fail;
|
221
|
+
|
222
|
+
if( maskw != width || maskh != height ) {
|
223
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
224
|
+
"swft:import-jpega() : mask has different size than jpeg image: %i/%i and %i/%i\n", width, height, maskw, maskh );
|
225
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
226
|
+
goto fail;
|
227
|
+
}
|
228
|
+
|
229
|
+
mask = readpng_get_image( 2.2, &channels, &rowbytes, &palette, &n_pal );
|
230
|
+
if( channels != 1 || rowbytes != width ) {
|
231
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
232
|
+
"swft:import-jpega() : mask is not 8bit grayscale\n");
|
233
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
234
|
+
goto fail;
|
235
|
+
}
|
236
|
+
|
237
|
+
mask_size = data_size;
|
238
|
+
if( compress( mask, width*height, maskdata, &mask_size ) ) {
|
239
|
+
data_size = size + ofs + mask_size;
|
240
|
+
} else {
|
241
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
242
|
+
"swft:import-jpega() : could not compress mask\n" );
|
243
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
244
|
+
goto fail;
|
245
|
+
}
|
246
|
+
|
247
|
+
swft_addData( node, (char*)data, data_size );
|
248
|
+
valuePush( ctx, xmlXPathNewNodeSet( (xmlNodePtr)doc ) );
|
249
|
+
|
250
|
+
fail:
|
251
|
+
if( fp ) fclose(fp);
|
252
|
+
if( data ) delete data;
|
253
|
+
if( jpegdata ) delete jpegdata;
|
254
|
+
}
|
255
|
+
|
@@ -0,0 +1,268 @@
|
|
1
|
+
#include <libxslt/extensions.h>
|
2
|
+
#include <libxslt/xsltutils.h>
|
3
|
+
#include <libxslt/variables.h>
|
4
|
+
#include <libxml/xpathInternals.h>
|
5
|
+
#include "swft.h"
|
6
|
+
#include <sys/types.h>
|
7
|
+
#include <sys/stat.h>
|
8
|
+
#include <cstring>
|
9
|
+
|
10
|
+
#define TMP_STRLEN 0xff
|
11
|
+
|
12
|
+
#define ERROR_NO_MP3 -1
|
13
|
+
#define ERROR_WRONG_SAMPLING_RATE -2
|
14
|
+
|
15
|
+
enum {
|
16
|
+
MPEG_V25 = 0,
|
17
|
+
MPEG_RESERVED,
|
18
|
+
MPEG_V2,
|
19
|
+
MPEG_V1,
|
20
|
+
};
|
21
|
+
|
22
|
+
const int mpegVersionBitrate[] = {
|
23
|
+
1, // V2.5, bitrates same as V2
|
24
|
+
-1,
|
25
|
+
1, // V2
|
26
|
+
0 // V1
|
27
|
+
};
|
28
|
+
|
29
|
+
// Only Layer3 is supported
|
30
|
+
const int mp3Bitrates[][15] = {
|
31
|
+
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}, // V1
|
32
|
+
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, // V2 & V2.5
|
33
|
+
};
|
34
|
+
|
35
|
+
|
36
|
+
/* As required in DefineSound, as a function of mpegVersion */
|
37
|
+
const int flashSamplingRates[] = {
|
38
|
+
1, // V25 -> 11025,
|
39
|
+
-1, // dummy
|
40
|
+
2, // V2 -> 22050,
|
41
|
+
3, // V1 -> 44100;
|
42
|
+
};
|
43
|
+
|
44
|
+
|
45
|
+
const int samplingRates[][4] = {
|
46
|
+
{11025, 12000, 8000, -1}, // V2.5
|
47
|
+
{ -1, -1, -1, -1}, // dummy
|
48
|
+
{22050, 24000, 12000, -1}, // V2
|
49
|
+
{44100, 48000, 32000, -1}, // V1
|
50
|
+
};
|
51
|
+
|
52
|
+
struct MP3Info {
|
53
|
+
int samplingRate;
|
54
|
+
int samplesPerFrame;
|
55
|
+
int flashSamplingRateFlag;
|
56
|
+
int frames;
|
57
|
+
int stereo;
|
58
|
+
bool validMP3;
|
59
|
+
bool wrongSamplingRate;
|
60
|
+
};
|
61
|
+
|
62
|
+
int findFrame( const unsigned char* data, int size, int start ) {
|
63
|
+
int pos = start;
|
64
|
+
|
65
|
+
while( pos < size ) {
|
66
|
+
if( data[pos] == 0xFF && (data[pos + 1] & 0xE0) == 0xE0 ) {
|
67
|
+
return pos;
|
68
|
+
}
|
69
|
+
pos++;
|
70
|
+
}
|
71
|
+
|
72
|
+
return -1;
|
73
|
+
}
|
74
|
+
|
75
|
+
int getFrameSize( const unsigned char* data, int size, int pos, MP3Info &info) {
|
76
|
+
if( pos + 2 >= size ) {
|
77
|
+
return ERROR_NO_MP3;
|
78
|
+
}
|
79
|
+
|
80
|
+
unsigned char c = data[pos + 1];
|
81
|
+
int mpegVersion = (c & 0x18) >> 3; // 0:V2.5 1:reserved 2:V2 3:V1
|
82
|
+
int layer = (c & 0x06) >> 1; // 1 means Layer III
|
83
|
+
|
84
|
+
// An MP3 file is Layer III, MPEG version any
|
85
|
+
if( layer != 1) {
|
86
|
+
fprintf(stderr, "Error: Layer should be III.\n");
|
87
|
+
return ERROR_NO_MP3;
|
88
|
+
}
|
89
|
+
|
90
|
+
if (mpegVersion == MPEG_RESERVED) {
|
91
|
+
fprintf(stderr, "Error: Unknown MPEG version (reserved).\n");
|
92
|
+
return ERROR_NO_MP3;
|
93
|
+
}
|
94
|
+
|
95
|
+
c = data[pos + 2];
|
96
|
+
int bitrate = (c & 0xF0) >> 4;
|
97
|
+
int samplingRate = (c & 0x0C) >> 2;
|
98
|
+
int padding = (c & 0x02) >> 1;
|
99
|
+
|
100
|
+
if (bitrate > 14) {
|
101
|
+
fprintf(stderr, "MP3 bitrate field invalid. Corrupt MP3 file?");
|
102
|
+
return ERROR_NO_MP3;
|
103
|
+
}
|
104
|
+
|
105
|
+
info.samplingRate = samplingRates[mpegVersion][samplingRate];
|
106
|
+
info.flashSamplingRateFlag = flashSamplingRates[mpegVersion];
|
107
|
+
|
108
|
+
if( samplingRate != 0 ) {
|
109
|
+
fprintf(stderr, "Sampling rate: %d\n", info.samplingRate);
|
110
|
+
fprintf(stderr, "Error: Flash only supports sampling rates of 44100, 22050 and 11025 Hz\n");
|
111
|
+
return ERROR_WRONG_SAMPLING_RATE;
|
112
|
+
}
|
113
|
+
|
114
|
+
info.samplesPerFrame = mpegVersion == MPEG_V1 ? 1152 : 576; // Since we deal with Layer III only
|
115
|
+
|
116
|
+
//Calculate the frame size in bytes
|
117
|
+
int br_table = mpegVersionBitrate[mpegVersion];
|
118
|
+
int frameSize = (info.samplesPerFrame / 8) * (mp3Bitrates[br_table][bitrate] * 1000) / info.samplingRate + padding;
|
119
|
+
|
120
|
+
return frameSize;
|
121
|
+
}
|
122
|
+
|
123
|
+
void getMP3Info( MP3Info& info, const unsigned char* data, int size ) {
|
124
|
+
info.frames = 0;
|
125
|
+
info.stereo = 0;
|
126
|
+
info.validMP3 = true;
|
127
|
+
info.wrongSamplingRate = false;
|
128
|
+
int pos = 0;
|
129
|
+
bool first = true;
|
130
|
+
|
131
|
+
while( (pos = findFrame( data, size, pos)) >= 0 ) {
|
132
|
+
int frameSize = getFrameSize( data, size, pos, info );
|
133
|
+
if( frameSize > 0 ) {
|
134
|
+
if( first ) {
|
135
|
+
if(pos + 3 < size) {
|
136
|
+
if((data[pos + 3] & 0xC0) != 0xC0)
|
137
|
+
info.stereo = 1;
|
138
|
+
}
|
139
|
+
first = false;
|
140
|
+
}
|
141
|
+
|
142
|
+
pos += frameSize;
|
143
|
+
info.frames++;
|
144
|
+
} else {
|
145
|
+
if ( frameSize == ERROR_WRONG_SAMPLING_RATE ) {
|
146
|
+
info.wrongSamplingRate = true;
|
147
|
+
} else {
|
148
|
+
info.validMP3 = false;
|
149
|
+
}
|
150
|
+
return;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
//no frames found -> no valid mp3
|
155
|
+
if( info.frames == 0 ) {
|
156
|
+
info.validMP3 = false;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
|
161
|
+
void swft_import_mp3( xmlXPathParserContextPtr ctx, int nargs ) {
|
162
|
+
xsltTransformContextPtr tctx;
|
163
|
+
xmlChar *filename;
|
164
|
+
xsltDocumentPtr xsltdoc;
|
165
|
+
xmlDocPtr doc = NULL;
|
166
|
+
xmlNodePtr node;
|
167
|
+
xmlXPathObjectPtr obj;
|
168
|
+
char tmp[TMP_STRLEN];
|
169
|
+
//data variables
|
170
|
+
unsigned char *data = NULL;
|
171
|
+
int size;
|
172
|
+
struct stat filestat;
|
173
|
+
|
174
|
+
xmlXPathStringFunction(ctx, 1);
|
175
|
+
if (ctx->value->type != XPATH_STRING) {
|
176
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
177
|
+
"swft:import-mp3() : invalid arg expecting a string\n");
|
178
|
+
ctx->error = XPATH_INVALID_TYPE;
|
179
|
+
return;
|
180
|
+
}
|
181
|
+
obj = valuePop(ctx);
|
182
|
+
if (obj->stringval == NULL) {
|
183
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
184
|
+
return;
|
185
|
+
}
|
186
|
+
|
187
|
+
tctx = xsltXPathGetTransformContext(ctx);
|
188
|
+
|
189
|
+
filename = obj->stringval;
|
190
|
+
|
191
|
+
bool quiet = true;
|
192
|
+
xmlXPathObjectPtr quietObj = xsltVariableLookup( tctx, (const xmlChar*)"quiet", NULL );
|
193
|
+
if( quietObj && quietObj->stringval ) { quiet = !strcmp("true",(const char*)quietObj->stringval ); };
|
194
|
+
|
195
|
+
|
196
|
+
FILE *fp = fopen( (const char *)filename, "rb" );
|
197
|
+
if( !fp ) {
|
198
|
+
xsltTransformError(xsltXPathGetTransformContext(ctx), NULL, NULL,
|
199
|
+
"swft:import-mp3() : failed to read file '%s'\n", (const char *)filename);
|
200
|
+
valuePush(ctx, xmlXPathNewNodeSet(NULL));
|
201
|
+
return;
|
202
|
+
}
|
203
|
+
|
204
|
+
doc = xmlNewDoc( (const xmlChar *)"1.0");
|
205
|
+
doc->xmlRootNode = xmlNewDocNode( doc, NULL, (const xmlChar *)"mp3", NULL );
|
206
|
+
node = doc->xmlRootNode;
|
207
|
+
|
208
|
+
swft_addFileName( node, (const char *)filename );
|
209
|
+
|
210
|
+
// get file size
|
211
|
+
if( stat( (const char *)filename, &filestat ) ) goto fail;
|
212
|
+
size = filestat.st_size;
|
213
|
+
|
214
|
+
// flash requires an initial latency value in front of the mp3 data
|
215
|
+
// TODO: check the meaning of this value and set it correctly
|
216
|
+
data = new unsigned char[size + 2];
|
217
|
+
data[0] = 0;
|
218
|
+
data[1] = 0;
|
219
|
+
|
220
|
+
// read data
|
221
|
+
if( fread( &data[2], 1, size, fp ) != size ) {
|
222
|
+
fprintf(stderr,"WARNING: could not read enough (%i) bytes for MP3 %s\n", size, filename );
|
223
|
+
goto fail;
|
224
|
+
}
|
225
|
+
|
226
|
+
if( size == 0 ) {
|
227
|
+
fprintf(stderr,"WARNING: MP3 %s is empty\n", filename );
|
228
|
+
goto fail;
|
229
|
+
}
|
230
|
+
|
231
|
+
MP3Info info;
|
232
|
+
getMP3Info( info, &data[2], size );
|
233
|
+
|
234
|
+
if( !info.validMP3 ) {
|
235
|
+
fprintf(stderr,"WARNING: this file is not a valid MP3 %s\n", filename );
|
236
|
+
goto fail;
|
237
|
+
}
|
238
|
+
|
239
|
+
if( info.wrongSamplingRate ) {
|
240
|
+
fprintf(stderr,"WARNING: MP3 file %s has a wrong sampling rate\n", filename );
|
241
|
+
goto fail;
|
242
|
+
}
|
243
|
+
|
244
|
+
xmlSetProp( node, (const xmlChar *)"format", (const xmlChar *)"2" ); //MP3
|
245
|
+
|
246
|
+
snprintf(tmp,TMP_STRLEN,"%i", info.flashSamplingRateFlag);
|
247
|
+
xmlSetProp( node, (const xmlChar *)"rate", (const xmlChar *)&tmp );
|
248
|
+
|
249
|
+
xmlSetProp( node, (const xmlChar *)"is16bit", (const xmlChar *)"1" ); //MP3 is always 16bit
|
250
|
+
|
251
|
+
snprintf(tmp,TMP_STRLEN,"%i", info.stereo);
|
252
|
+
xmlSetProp( node, (const xmlChar *)"stereo", (const xmlChar *)&tmp );
|
253
|
+
|
254
|
+
snprintf(tmp,TMP_STRLEN,"%i", info.frames * info.samplesPerFrame);
|
255
|
+
xmlSetProp( node, (const xmlChar *)"samples", (const xmlChar *)&tmp );
|
256
|
+
|
257
|
+
if( !quiet ) {
|
258
|
+
fprintf(stderr, "Importing MP3: '%s'\n", filename);
|
259
|
+
}
|
260
|
+
|
261
|
+
swft_addData( node, (char*)data, size + 2 );
|
262
|
+
valuePush( ctx, xmlXPathNewNodeSet( (xmlNodePtr)doc ) );
|
263
|
+
|
264
|
+
fail:
|
265
|
+
if( fp ) fclose(fp);
|
266
|
+
if( data ) delete data;
|
267
|
+
}
|
268
|
+
|