提取 SWF 中的音频!! - -7℃→百一的生活 - 博客园
来源:百度文库 编辑:神马文学网 时间:2024/10/07 00:23:16
提取 SWF 中的音频!!
好久没有更新博客了, 原因有好多, 天气热人烦躁是一个方面, 最大原因是自己懒得写. 本篇博文其实一个月之前就计划写了, 但每次开了头就感觉没有必要写下去了. 不废话了切入正题.
从 SWF 中提取音频的起因是想下载国内某音乐推荐站的音乐, 但赖于此站点推荐的音乐基本都被转化成了 SWF 格式的文件形式, 所以突发奇想能不能把 SWF 的音频给提取出来. 经过资料的搜集和准备, 最后还是成功了一半, 虽然并不是所有 SWF 内的音频可以正确的提取出来, 但对 MP3 格式的音频提取已经做的很完美了.
首先找到的一个处理 SWF 的类库 SwfDotNet, 本类库支持读写 SWF 7.0 之前(包括)的文件, 官方网站: http://sourceforge.net/projects/swfdotnet/. 基于 GPL 协议的开源 .Net 类库, 本类库RC1 已经好长时间了, 两年已经没有人维护了, 虽然不是很完美不过已经足够了.
因为在提取的中途出了点小波折, 所以后来又找到了 SWF 的格式规范 SWF 文件格式中音频可以分为两大类: SWF 中的各个元素在其二进制文件中都是以TAG的形式存在的. 对于音频来说涉及到的TAG大概有以下几种: 其中只有 SoundStreamBlock Tag 和 DefineSound Tag 会包含实际的音频流, 其他的都是用来标识用的. SoundStreamBlock TAG 存放 Streaming sounds 类型的音频. 在某个 SWF 文件时间轴内如果有 SoundStreamBlock 格式的TAG , 那么在第一个 SoundStreamBlock TAG 之前必须包括一个 SoundStreamHead2 TAG 或者 SoundStreamHead TAG, 只有这样才能标记其后的 SoundStreamBlock TAG 内的音频格式/记住回放格式/每个SoundStreamBlock TAG的平均取样数等信息.往往, 对于一个 SWF 文件有多个SoundStreamBlock TAG 组成一个组来共同存放一个音频文件. SoundStreamHead TAG 内的StreamSoundCompression 字段标识其后的 SoundStreamBlock TAG 包含的音频格式, 可以存放以下音频格式: 1 = ADPCM SoundStreamHead2 TAG中的StreamSoundCompression 字段标识的音频格式可以有: 0 = uncompressed SoundStreamBlock TAG 的 StreamSoundData 字段存放音频的实际数据流, 其音频数据流的存放方式依赖于其前的SoundStreamHead TAG或者SoundStreamHead2 TAG 的StreamSoundCompression 字段标识, 对于不同的音频格式其存放的规则不一样. 这里有一个小小的问题, 在实际测试的时候并没有发现用SoundStreamHead2 TAG 标识的SoundStreamBlock TAG , 大概是自己对 Event sounds 格式的音频以 DefineSound 的TAG 形式存在, 并且一个 SWF 文件可以包含多个 DefineSound TAG. 每个 DefineSound TAG 存放一个单独的音频文件, 每个 DefineSound TAG 包含一个本 TAG 内存放音频的格式的属性(SoundFormat). DefineSound TAG 存放的音频格式有: 0 = uncompressed 似乎和SoundStreamHead2 TAG 标识的一样. 测试发现在一个拥有DefineSound TAG 的SWF文件之前往往有一个SoundStreamHead2 TAG , 其后紧跟第一个DefineSound TAG. SwfDotNet 在处理 DefineSound TAG 的时候直接定义了一个 DecompileToFile 方法, 可以把其中的音频直接导出到一个文件. StartSound Tag 和 StartSound2 Tag 跟在 DefineSound TAG 之后用来开始播放音频, 对实际的提取没有多大的帮助. 根据以上个规制: 写的提取代码为以下: //导出 Sound if (_SwfFile.Tags != null) { foreach (BaseTag tag in _SwfFile.Tags) { //流方式的Sound if (tag is SoundStreamHeadTag) { SwfProcessUtility.OutSoundStreamBlock((tag as SoundStreamHeadTag).StreamSoundCompression, outPath, _SwfFile.Tags); //break; } //直接的声音 可以直接导出 if (tag is DefineSoundTag) { SwfProcessUtility.OutDefineSoundTag((tag as DefineSoundTag), outPath); } } } SwfProcessUtility 类中定义了多个静态方法来出来 TAG, 源代码为: using System; using System.Collections.Generic; using System.Text; using System.IO; using SwfDotNet.IO; using SwfDotNet.IO.Tags; namespace TUP.SwfProcessing.Sound { /// /// Swf 处理类 /// internal static class SwfProcessUtility { /// /// 读取指定的 SWF 文件 /// /// /// public static Swf ReadSwfFile(string swfFilePath) { SwfReader swfReader = new SwfReader(swfFilePath); return swfReader.ReadSwf(); } /// /// 将 tags 内 SoundStreamBlock 合并到一个文件内导出 /// /// /// /// public static void OutSoundStreamBlock(uint _StreamSoundCompression, string outPath, BaseTagCollection tags) { string outFileFileName = "{0}.{1}"; //1 = ADPCM 2 = MP3 //MP3 Sound if (_StreamSoundCompression == 2) { outFileFileName = string.Format(outFileFileName, Guid.NewGuid(), "mp3"); using (FileStream stream = new FileStream(Path.Combine(outPath, outFileFileName), FileMode.OpenOrCreate, FileAccess.Write)) { foreach (BaseTag tag in tags) { if (tag is SoundStreamBlockTag) { byte[] buffer = null; buffer = (tag as SoundStreamBlockTag).SoundData; //偏移 4 位 //SampleCount UI16 2 byte //SeekSamples SI16 2 byte stream.Write(buffer, 4, buffer.Length - 4); stream.Flush(); } } } } //ADPCM Sound //此方法处理 的 Sound 不能使用 if (_StreamSoundCompression == 1) { outFileFileName = string.Format(outFileFileName, Guid.NewGuid(), "ADPCM"); using (FileStream stream = new FileStream(Path.Combine(outPath, outFileFileName), FileMode.OpenOrCreate, FileAccess.Write)) { foreach (BaseTag tag in tags) { if (tag is SoundStreamBlockTag) { byte[] buffer = null; buffer = (tag as SoundStreamBlockTag).SoundData; stream.Write(buffer, 0, buffer.Length - 0); stream.Flush(); } } } } } /// /// 将 DefineSoundTag 中的音频导出 /// 实际上只对MP3格式Sound有作用 /// /// /// public static void OutDefineSoundTag(DefineSoundTag _DefineSoundTag, string outPath) { string outFileFileName = "{0}.{1}"; outFileFileName = string.Format(outFileFileName, Guid.NewGuid(), _DefineSoundTag.SoundFormat); _DefineSoundTag.DecompileToFile(Path.Combine(outPath, outFileFileName)); } } } 上面的代码也不多解释了, 注释很清楚. 其实关键的是 SwfProcessUtility 方法中的两个静态方法. 对于 DefineSound TAG 中的音频只是将其直接导出到文件并以其类型作为扩展名. 而 OutSoundStreamBlock 方法在处理 SoundStreamBlock TAG 的时候对于MP3 格式的音频做了特殊处理, 原因需要说明一下, 这个也就是之前提到的波折. SoundStreamHead TAG 标识了之后的音频格式, 其后的 SoundStreamBlock TAG 的 SoundData 字段会因为不同的音频格式做不同的存放处理. 对于 MP3 格式的音频大致的存放规律为: 所以就有了代码内的: stream.Write(buffer, 4, buffer.Length - 4); 对每一个 SoundStreamBlock TAG 的 SoundData 字段都偏移 4 个字节, 把之后的字节依次的写到一个输出文件中, 就可以导出一个MP3文件. 上面大致也就是思路和结果, 可以完美的导出 MP3 文件. 不管是 Event 类型的还是 Stream 类型的音频. 对于文章开始的音乐推荐站来说, 其 SWF 格式为 V5 版, 其内的音频大多是 Stream 类型的MP3 格式. 这个地方还有一个没有搞清楚的地方, 因为 MP3 文件内并不是只存放 MP3 的帧, 还有头信息, 不知道SWF是咋的处理的, 可能需要仔细分析 大致就这么多, 希望能给看到的朋友以帮助. 这里提供源代码下载. 开发环境 VS2008, .NET 2.0 . 下载: [http://ishare.iask.sina.com.cn/cgi-bin/fileid.cgi?fileid=4312000]
SWF 4 和以后版本:
2 = MP3
1 = ADPCM
SWF 4 或以后版本:
2 = MP3
3 = uncompressed little-endian
SWF 6 或以后版本:
6 = Nellymoser
1 = ADPCM
SWF 4 或以后版本:
2 = MP3
3 = uncompressed little-endian
SWF 6 或以后版本:
6 = Nellymoser
认识一下,^_^
回复 引用 查看 #2楼Justin 在2008-09-03 12:55说: lz这方面很有研究!
回复 引用 查看 #3楼testsssssssss[未注册用户] 在2008-09-03 13:02说: 这个文章应该是转载的,我看过此篇文章内容完全一样
回复 引用 #4楼nasa 在2008-09-03 13:24说: --引用--------------------------------------------------
testsssssssss: 这个文章应该是转载的,我看过此篇文章内容完全一样
--------------------------------------------------------
我看到过 是这个
http://zhq.ahau.edu.cn/blog/article/452.htm