Skip to content

Instantly share code, notes, and snippets.

@banjun
Created January 15, 2009 13:31
Show Gist options
  • Save banjun/47404 to your computer and use it in GitHub Desktop.
Save banjun/47404 to your computer and use it in GitHub Desktop.
extract mp3 from swf
static unsigned int readUInt(unsigned char *p, unsigned int offset, unsigned int bytes)
{
unsigned int result = 0;
int i;
for (i = 0; i < bytes; ++i){
result += p[offset + i] << ((bytes - i - 1) * 8);
}
return result;
}
static unsigned int readUIntLE(unsigned char *p, unsigned int offset, unsigned int bytes)
{
unsigned int result = 0;
int i;
for (i = 0; i < bytes; ++i){
result += p[offset + i] << (i * 8);
}
return result;
}
static uint32 readUI32(unsigned char *p, unsigned int offset){ return readUInt(p, offset, 4); }
static uint16 readUInt16LE(uint8 **p){ *p += 2; return (uint16)readUIntLE(*p, -2, 2); }
static float readUFixed16LE(uint8 **p){
uint16 data = readUInt16LE(p);
uint8 *datap = (uint8*)(&data);
return (float)datap[1] + (float)datap[0] / 255.0;
}
static uint32 readBits(uint8 **p, uint8 *bitOffset, uint8 bitLength)
{
uint32 data= readUI32(*p, 0);
uint32 result = ((data << *bitOffset) & 0xffffffff) >> (32 - bitLength);
*p += (*bitOffset + bitLength) / 8;
*bitOffset = (*bitOffset + bitLength) % 8;
return result;
}
static NSRect readRect(uint8 **p, uint8 *bitOffset)
{
// Nbits UB[5]
// Xmin SB[Nbits]
// Xmax SB[Nbits]
// Ymin SB[Nbits]
// Ymax SB[Nbits]
const uint8 nbitsSize = 5;
uint8 nbits = readBits(p, bitOffset, nbitsSize);
uint32 xmin = readBits(p, bitOffset, nbits);
uint32 xmax = readBits(p, bitOffset, nbits);
uint32 ymin = readBits(p, bitOffset, nbits);
uint32 ymax = readBits(p, bitOffset, nbits);
if (bitOffset > 0){ *p += 1; *bitOffset = 0; } // align to byte
return NSMakeRect(xmin, ymin, xmax, ymax);
}
enum {
kTagTypeEnd = 0,
kTagTypeSoundStreamHead = 18,
kTagTypeSoundStreamBlock = 19,
};
static const uint8 kSoundFormatMP3 = 2;
// extract mp3 from swf to file
- (void)extractMP3:(NSData *)data toFile:(NSString *)filepath
{
FILE *outputFP = fopen([filepath UTF8String], "w");
LOG(@"extractToFile:%@, fp = %p", filepath, outputFP);
unsigned char *p = (unsigned char *)[data bytes];
unsigned char *dataBoundary = p + [data length];
uint8 bitOffset = 0;
BOOL compressed = (p[0] == 'C'); // when compressed, file signature is 'CWS'
uint32 swfSize = readUIntLE(p, 4, 4); // encoded in LE in swf.
LOG(@"compressed = %d, uncompressed swf file size = %u bytes.", compressed, swfSize);
const int kNotCompressedLength = 8; // first 8 bytes is not compressed.
p += kNotCompressedLength;
if (compressed){
// for uncompressing test, enable OUTPUT_UNCOMPRESSED_SWF.
// #define OUTPUT_UNCOMPRESSED_SWF
#ifdef OUTPUT_UNCOMPRESSED_SWF
NSMutableData *notCompressedData = [NSMutableData dataWithData:[data subdataWithRange:NSMakeRange(0, kNotCompressedLength)]];
NSString *outputFilename = [@"~/Downloads/uncompressed.swf" stringByExpandingTildeInPath];
unsigned char bytes[] = {'F'}; // file signature 'CWS' to 'FWS'
[notCompressedData replaceBytesInRange:NSMakeRange(0,1) withBytes:bytes length:1];
#endif
data = [data zlibInflateFromOffset:kNotCompressedLength];
LOG(@"uncompressed data = %d bytes", [data length]);
p = (unsigned char *)[data bytes];
dataBoundary = p + [data length];
#ifdef OUTPUT_UNCOMPRESSED_SWF
[notCompressedData appendData:data];
[notCompressedData writeToFile:outputFilename atomically:YES];
LOG(@"wrote uncompressed data to %@", outputFilename);
#undef OUTPUT_UNCOMPRESSED_SWF
#endif
}
// now p is swf data, starting from 9 bytes (omitted kNotCompressedLength bytes).
// frame rect is variable byte-size.
NSRect frame = readRect(&p, &bitOffset);
float frameRate = readUFixed16LE(&p);
uint16 frameCount = readUInt16LE(&p);
LOG(@"frame = %@, frameRate = %f, frameCount = %d, bitOffset = %d", NSStringFromRect(frame), frameRate, frameCount, bitOffset);
// end of header.
BOOL foundMP3 = NO;
while (p <= dataBoundary){
uint16 tagCodeAndLength = readUInt16LE(&p);
uint16 tagType = tagCodeAndLength >> 6; // upper 10 bits.
sint32 tagLength = tagCodeAndLength & 0x3f; // lower 6 bits.
if (tagLength == 0x3f){ // long tag
// length is following SI32.
tagLength = readUIntLE(p, 0, 4); p += 4;
}
unsigned char *nextTagHead = p + tagLength;
// NSLog(@"tag:%d, type = %d, length = %d", tagCodeAndLength, tagType, tagLength);
if (tagType == kTagTypeEnd){ LOG(@"found end tag."); break; }
if (tagType == kTagTypeSoundStreamHead){
LOG(@"found SoundStreamHead.");
p += 1; // skip some fields.
uint8 streamSoundCompression = readBits(&p, &bitOffset, 4); p +=1, bitOffset = 0;
if (streamSoundCompression == kSoundFormatMP3){
LOG(@"found mp3.");
foundMP3 = YES;
} else{
LOG(@"sound is not mp3. cancelled.");
break;
}
}
if (tagType == kTagTypeSoundStreamBlock){
unsigned char *mp3Frames = p + 2; // skip SeekSamples (SI16)
// NSLog(@"found kTagTypeSoundStreamBlock. p = %p, mp3Frames = %p", p, mp3Frames);
if (foundMP3){
unsigned char *mp3Data = mp3Frames + 2; // skip [SyncWord, MpegVersion, Layer, ProtectionBit]
// the following is mp3 Data.
uint32 sampleSize = nextTagHead - mp3Data;
fwrite(mp3Data, sizeof(unsigned char), sampleSize, outputFP);
}
}
p = nextTagHead;
}
fclose(outputFP);
LOG(@"finished extracting audio from swf.");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment