打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
提取 SWF 中的音频!! - -7℃→百一的生活 - 博客园

提取 SWF 中的音频!!

 

好久没有更新博客了, 原因有好多, 天气热人烦躁是一个方面, 最大原因是自己懒得写. 本篇博文其实一个月之前就计划写了, 但每次开了头就感觉没有必要写下去了. 不废话了切入正题.

SWF 中提取音频的起因是想下载国内某音乐推荐站的音乐, 但赖于此站点推荐的音乐基本都被转化成了 SWF 格式的文件形式, 所以突发奇想能不能把 SWF 的音频给提取出来. 经过资料的搜集和准备, 最后还是成功了一半, 虽然并不是所有 SWF 内的音频可以正确的提取出来, 但对 MP3 格式的音频提取已经做的很完美了.

首先找到的一个处理 SWF 的类库 SwfDotNet, 本类库支持读写 SWF 7.0 之前(包括)的文件, 官方网站: http://sourceforge.net/projects/swfdotnet/. 基于 GPL 协议的开源 .Net 类库, 本类库RC1 已经好长时间了, 两年已经没有人维护了, 虽然不是很完美不过已经足够了.

因为在提取的中途出了点小波折, 所以后来又找到了 SWF 的格式规范<SWF File Format Specification V9>. Adobe 官方得到.

SWF 文件格式中音频可以分为两大类:

  • Event sounds
  • Streaming sounds

SWF 中的各个元素在其二进制文件中都是以TAG的形式存在的对于音频来说涉及到的TAG大概有以下几种:

  • SoundStreamHead2 Tag
  • SoundStreamHead Tag
  • SoundStreamBlock Tag
  • StartSound Tag
  • StartSound2 Tag
  • DefineSound 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
SWF 4
和以后版本:
2 = MP3

SoundStreamHead2 TAG中的StreamSoundCompression 字段标识的音频格式可以有:

0 = uncompressed
1 = ADPCM
SWF 4
或以后版本:
2 = MP3
3 = uncompressed little-endian
SWF 6
或以后版本:
6 = Nellymoser

SoundStreamBlock  TAG StreamSoundData 字段存放音频的实际数据流, 其音频数据流的存放方式依赖于其前的SoundStreamHead TAG或者SoundStreamHead2 TAG StreamSoundCompression 字段标识, 对于不同的音频格式其存放的规则不一样.

这里有一个小小的问题, 在实际测试的时候并没有发现用SoundStreamHead2 TAG 标识的SoundStreamBlock  TAG , 大概是自己对 <SWF 格式规范>没有读好, 读懂.

 

Event sounds 格式的音频以 DefineSound TAG 形式存在, 并且一个 SWF 文件可以包含多个 DefineSound TAG.  每个 DefineSound TAG 存放一个单独的音频文件, 每个 DefineSound TAG 包含一个本 TAG 内存放音频的格式的属性(SoundFormat). DefineSound TAG 存放的音频格式有:

0 = uncompressed
1 = ADPCM
SWF 4
或以后版本:
2 = MP3
3 = uncompressed little-endian
SWF 6
或以后版本:
6 = Nellymoser

 

似乎和SoundStreamHead2 TAG 标识的一样. 测试发现在一个拥有DefineSound TAG SWF文件之前往往有一个SoundStreamHead2 TAG , 其后紧跟第一个DefineSound TAG. SwfDotNet 在处理 DefineSound TAG 的时候直接定义了一个 DecompileToFile 方法, 可以把其中的音频直接导出到一个文件.

StartSound Tag StartSound2 Tag 跟在 DefineSound TAG 之后用来开始播放音频, 对实际的提取没有多大的帮助.

根据以上个规制:

  • SoundStreamHeadTag 之后跟多个SoundStreamBlock  TAG 包含一个音频文件;
  • SoundStreamBlock  TAG SoundData 字段存放音频文件数据;
  • 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

{

    /// <summary>

    /// Swf 处理类

    /// </summary>

    internal static class SwfProcessUtility

    {

        /// <summary>

        /// 读取指定的 SWF 文件

        /// </summary>

        /// <param name="swfFilePath"></param>

        /// <returns></returns>

        public static Swf ReadSwfFile(string swfFilePath)

        {

            SwfReader swfReader = new SwfReader(swfFilePath);

            return swfReader.ReadSwf();

        }

        /// <summary>

        /// 将 tags 内 SoundStreamBlock 合并到一个文件内导出

        /// </summary>

        /// <param name="_StreamSoundCompression"></param>

        /// <param name="outPath"></param>

        /// <param name="tags"></param>

        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();

                        }

                    }

                }

            }

        }

        /// <summary>

        /// 将 DefineSoundTag 中的音频导出

        /// 实际上只对MP3格式Sound有作用

        /// </summary>

        /// <param name="_DefineSoundTag"></param>

        /// <param name="outPath"></param>

        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 格式的音频大致的存放规律为:

  • SoundStreamBlock TAG SoundData 字段存放一个 MP3STREAMSOUNDDATA 结构的数据;
  • MP3STREAMSOUNDDATA 拥有一个 UI16 格式的 SampleCount 字段;
  • MP3STREAMSOUNDDATA 第二个字段 Mp3SoundData 存放具体的音频, 其数据结构为 MP3SOUNDDATA;
  • MP3SOUNDDATA 的结构有两个字段, 第一个为 SI16 格式的 SeekSamples, 第二个为 Mp3Frames.
  • Mp3Frames 为实际音频格式的 MP3 的帧, 可以有 0 个或多个, 也就是一个数组;
  • UI16 SI16 格式都为 2 byte , 加起来 4 byte;

所以就有了代码内的:

stream.Write(buffer, 4, buffer.Length - 4);

对每一个 SoundStreamBlock TAG SoundData  字段都偏移 4 个字节, 把之后的字节依次的写到一个输出文件中, 就可以导出一个MP3文件.

上面大致也就是思路和结果, 可以完美的导出 MP3 文件. 不管是 Event 类型的还是 Stream 类型的音频. 对于文章开始的音乐推荐站来说, SWF 格式为 V5 , 其内的音频大多是 Stream 类型的MP3 格式.

这个地方还有一个没有搞清楚的地方, 因为 MP3 文件内并不是只存放 MP3 的帧, 还有头信息, 不知道SWF是咋的处理的, 可能需要仔细分析<SWF 格式规范>. 为啥每个 SoundData 偏移之后, 导出的文件就可以使用.

大致就这么多, 希望能给看到的朋友以帮助. 这里提供源代码下载. 开发环境 VS2008, .NET 2.0 .

下载: [http://ishare.iask.sina.com.cn/cgi-bin/fileid.cgi?fileid=4312000]

 

0
0
(请您对文章做出评价)
posted @ 2008-09-03 12:22 青岛小帅哥 阅读(2135) 评论(5)  编辑 收藏 网摘 所属分类: flash类 swf文件
#1楼巫云       在2008-09-03 12:44说:
我也是青岛的哦!
认识一下,^_^

  回复  引用  查看    
#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
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
使用千千静听 轻松从视频中提取音频
分离视频中的音频文件
了解播放器常见视频格式
怎样下载网站无法下载的音频、swf文件
巧用千千静听实现音频格式转换
用千千静听压缩音频文件及格式转换 ----好好
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服