Created
January 15, 2009 13:31
-
-
Save banjun/47404 to your computer and use it in GitHub Desktop.
extract mp3 from swf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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