wma文件结构示意图

格式的简单说明:

如图1,每一个WMA文件,它的头16个字节是固定的,为十六进制的“30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C”,用来标识这个是否为WMA文件。接下来的8个字节为一个整数,表示整个WMA文件头部的大小,这个头部里面包含了Tag信息等所有非音频信息,头部后面的是音频信息,我们在这里就不深入了解了。那个整数接下来的6个字节还没搞清楚是什么用的,不过不影响我们对Tag信息的读写。
 
    也就是说从文件开始偏移量为31开始,里面存放了很多帧,有我们需要的标准Tag信息,扩展Tag信息,WMA文件控制信息等等。每个帧不是等长的,但是帧头是固定的24个字节,其中前16字节是用来标识这个帧的名字,后8个字节是用来表示这个帧(包括帧头)的大小。这一点和MP3文件的ID3V2信息比 较像。
    由于我们只需要读写Tag信息,而Tag信息又分别保存在两个帧里,分别为标准Tag帧和扩展Tag帧,所有我们只需要处理这两个帧,其他帧完全可以根据获得的帧长度来跳过。
    如图2,标准Tag帧只包含歌曲标题,艺术家,版权,备注四个内容。它的帧名是十六进制的“3326 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C”,在24个字节的帧头后紧跟着5个分别为2个字节的整数,分别表示歌曲标题,艺术家,版权,备注,未知信息的大小,未知信息大部分情况下是不使用的,即它的大小为0的。

在这10个字节后,这五个信息的内容就按顺序存放了。记住,在WMA文件里,所有的文字都是按Unicode宽字符的编码方式储存的,而且每个字符串后面都有一个0x00 0x00结束字符的。
    如图3,再看扩展Tag帧,这里就比较麻烦了,里面包含的信息的个数是不确定的,每个信息也是按照像帧一样的方式组织起来的。扩展Tag帧的帧名是十六进制的“40 A4 D0 D2 07 E3 D2 11 97 F000 A0 C9 5E A8 50”,在24字节的帧头(HeadFlag:16,HeadSize:8)后先有一个两个字节的整数表示这个帧里一共有的扩展信息个数(ExNo)。紧接着是扩展信息。

如图4,每一个扩展信息包含扩展信息名字大小(2字节)和对应的内容。先有一个两个字节的整数来表示扩展名字信息的大小,接着是扩展信息名称,然后有一个两个字节的整数标志(Flag)。然后又是一个两个字节的整数,表示值的大小。接着就是这个值。
    当扩展信息名字为WMFSDKVersion时,这个值表示的是这个WMA文件的版本;

当扩展信息名字为WM/AlbumTitle时,这个值代表的就是专辑名;

当扩展信息名字为WM/Genre时,这个值代表的就是流派;

同理,很容易从扩展信息的名字看出这个值的用途的。这些扩展信息的名字和值几乎都是用Unicode的字符串来存储的,到现在为止只发现对下面两个情况例外。(关于所有扩展信息的名字可以从很多地方查到,比如SDK帮助,MSDN)
    下面再来看看那个标志Flag,这个基本上是为没什么用的(通常值为0),只对WM/TrackNumber和WM/Track这两个扩展信息名字有用,当Flag为3的时候后面的值(也就是曲目信息)是以4个字节的整数的形式表示,当Flag为0的时候,曲目信息是以普通的字符串形式表示的

在研究wma格式的时候发现,它有一个帧是 全都是 0 的,相当于缓冲区。如果写入的歌名比原来长的话,就减少缓冲区大小,歌名短就增加缓冲区。这样就可以保持文件头的大小不变,每次更新的话只需要重写文件头,不需要重写音频数据。这6个 字节其中前面4个字节应该为总标签帧数。(猜测)理由如下:  个人写了个更改WMA头文件的程序,为WMA增 加了一个自定义帧,插入后的结果是该文件无法播放,遍查不得其果,想到这6个BYTE可能有所相关,就对比了两个WMA文件,发现这6个BYTE中头一个BYTE不一样,猜测这个数据应该为帧数,于是更改该数据,发现更改后WMA文 件解码正常。后经对比,证实这个数据确实为帧数,但唯一不能确定的是这个数据究竟是否是int型、short型、又或者是BYTE型。从文件上看个人认为是int型的。

WMV格式浅析

WMV,全称Windows Media Video,是微软为其开发的若干音视频编解码器推出的视频压缩文件格式。WMV,也是一种编解码器,最早 设计用于互联网流媒体应用,主要针对的竞争对手就是RealVideo。

·    WMV已经是开放标准

2003年,微软基于其WMV9 codec编写了一份视频编解码规格书,并提交给SMPTE(美国影视技术人员协会)申请为标准。2006年3月份,该标准正式通过,正式标准名 为SMPTE 421M,其非正式名VC-1。自此,VC-1成为了支持BD-ROM和HD DVD-ROM的三大视频编解码标准之一。

·    容器格式

很多人搞不清楚ASF和WMV有什么区别。在大多数情况下,WMV文件都是用ASF容器格式来封装的。ASF,全称是AdvancedSystem Format,也是微软专门针对流媒体开发的容器格式。使用.wmv扩展名的文件,代表这是一个使用WMV编解码器的ASF格式文件。一般来说,与WMV视频交织在一起的音频编码都是WMA(Windows Media Audio)。当然也有例外,偶尔会使用ACELP.net音频编码。微软建议,如果ASF格式文件中包含了非微软的编解码 器,最好使用.asf扩展名。

虽然WMV一般都是用ASF格式来封装,但是其也可以放入AVI和Matroska(MKV)格式中,文件扩展名也相对应为.avi和.mkv。

·    视频质量

微软声称,WMV 9的压缩比超过MPEG-4的2倍,MPEG-2的3倍。微软还声称,WMV 9的压缩效率相对WMV 8提高了15-50%。不过,2005年1月份的一份测试报告表明WMV 9的压缩效率比WMV 8要差。

·    WMV相关软件

很多软件都能播放WMV文件,比如Windows Media Player,PowerDVD,RealPlayer,MPlayer,VLCMedia Player,Zoom Player和MediaPlayer Classic。能够对WMV进行编码的软件包括WindowsMovie Maker,Windows Media Encoder,Microsoft Expression Encoder,以及AdobePremiere Pro。如果你有别的格式的文件,希望转换成WMV格式的视频文件,在PC或Zune上播放,那就去下载一个支持WMV转换的Video Converter,有支持所有格式转换的Total VideoConverter,也有专门针对WMV的WMV Converter或Video to ZuneConverter,到Google上搜索或者到download.com上查找,有很多。

下面来介绍ASF文件格式

什么是ASF
    ASF 是 advance streaming format的缩写。是微软为windows98所开发的串流多媒体文件格式。也是windows media的核心。
    ASF是一种数据格式,音频,视频,图像以及控制命令脚本等多媒体信息通过这种格式,以网络数据包的形式传输,实现流式多媒体内容发布。

ASF优势

体积小,适合网络传输。
    数据组合形式灵活,可以将图形,声音和动画数据组合成一个ASF格式文件。也可以将其他格式的视频和音频转为ASF 格式等。
    在ASF视频中可以带有命令代码,用户指定在到达视频或音频的某个时间后执行某个事件或操作。

ASF特点

可扩展的媒体类型- ASF文件允许制作者很容易地定义新的媒体类型。ASF格式提供了非常有效的灵活地定义符合ASF文件格式定义的新的媒体流类型。任一存储的媒体流逻辑上都是独立于其他媒体流的,除非在文件头部分明显地定义了其与另一媒体流的关系。
    部件下载-特定的有关播放部件的信息(如,解压缩算法和播放器)能够存储在ASF 文件头部分,这些信息能够为客户机用来找到合适的所需的播放部件的版本---如果它们没有在客户机上安装。
    可伸缩的媒体类型- ASF是 设计用来表示可伸缩的媒体类型的\"带宽\"之间的依赖关系。ASF存 储各个带宽就像一个单独的媒体流。媒 体流之间的依赖关系存储在文件头部分,为客户机以一个独立于压缩的方式解释可伸缩的选项提供了丰富的信息流的优先级化- 现代的多媒体传输系统能够动态地调整以适应网络资源紧张的情况(如,带宽不足)。多媒体内容的制作者要能够根据流的优先级表达他们的参考信息,如最低保证音频流的传输。随着可伸缩媒体类型的出现,流的优先级的安排变得复杂起来,因为在制作的时候很难决定各媒体流的顺序。ASF允许内容制作者有效地表达他们的意见(有关媒体的优先级),甚至在可伸缩的媒体类型出现的情况下也可以.
    多语言- ASF设计为支持多语言。媒体流能 够可选地指示所含媒体的语言。这个功能常用于音频和文本流。一个多语言ASF文件指的是包含不同语言版本的同一内容的一系列媒体流,其允许 客户机在播放的过程中选择最合适的版本。
    目录信息- ASF提供可继续扩展的目录信息的功能,该功能的扩展性和灵活性都非常好。所有的目录信息都以无格式编码的 形式存储在文件头部分,并且支持多语言,如果需要,目录信息既可预先定义(如,作者和标题),也可 以是制作者自定义。目录信息功能既可 以用于整个文件也可以用于单个媒体流。

ASF结构

ASF 由三个主要部分组成。Header Object ,DataObject ,Index Object。
    Headerobject:主要描述了数据的一些信息。例如作者,曲目信息,用户加入的命令信息,码流的码率等信息。Headerobject里包含很多其他的子object。每个object相当于一个信息描述符。这些object由GUID来区分。
    Data object:存储数据。
    Index object:存放时间索引。有两种方式:simple index object和index object。

Header Object

?      Header object的作用主要体现在 以下个方面:

l       在ASF文件开始提供了一个全局的GUID。

l       包含所有在data object中需要说明的信息。

l       在ASF的三层结构中,只有header object可以携带其他ASF objects。

?      File properties object:包含全局文件信息。

?      Stream properties object:定义了一个数字媒体码流和他的特征。

?      Header extension object:支持为一个ASF文件添加新功能,以便向后兼容。

?      Content Description Object:包含一些目录信息。

?      Script command object: 包含一些用户的指令。这些指令可以在playback时候被执行。

?      Marker object:提供在一个文件 内的指定的跳跃点。(jump points)

注意:1)在header object中的objects可以以任何的顺序排列。

2)在一个header object中必须包含一个file properties object,一个header extension object 和至少一个streamproperties object。

Header Object 结构

?      Object ID:16字节。值为:30 26 B2 75 8E 66 CF 11 A6 D9 00 AA00 62 CE 6C

?      Object size: 8字节,描述了object总长度,包含16字节的ID和8字节的size。至少是30字节。

?      Number of header objects:描述了header object中包含的objects的个数。不包含本身,同时,header extension object只能算作一个object,这里的计数不包含header extension object中的subobjects。

?      Reserved1:忽略,固定值为0x1

?      Reserved2:忽略,固定值为0x2

Header Object 之 Fileproperties object

?

?      Object ID:16字节唯一标识。A1 DC AB 8C 47 A9 CF 11 8E E4 00 C00C 20 53 65

注意:所有的ASF对象和语法(包含data packet headers) 都是以little-endian字节序。但是ASF文件的data packet中包含的数字媒体流数据的字节序可以随意。

ASF Data Object

?      Data object:由ASF datapacket组 成。Data packet是一个定长的数据包。每一个数据包携带一个或多个数字媒体流。Data packet在data object中按照发送 时间顺序保存。也就是一个data packet可能包含来自不同的码流的中的数据。Data packet通过entry objects来组织 管理来自不同码流的数据。

?      Data packet:dataobject的组成单 位。包含信息

l       一个data packet中一个或多 个有效负载类型

l       Data packet长度

l       时钟信息

l       附加的采样信息,例如时戳信息

Data Object structure

?      Object ID:16字节。36 26 B2 75 8E 66 CF 11 A6 D9 00 AA 0062 CE 6C

?      File ID:在一个ASF文件中唯一。当ASF文件修改时,这个值也会改变。这个值和Header Object中的一个File ID相同。

?      Total Data Packets:在一个DataObject中的ASF data packet 的 个数。必须和File Properties object中的Data packet count的值相等。

?      Reserved:保留,值为0x0101

ASF Data Packet Structure

Error correction data structure

?      Error correction data length:当Error correction length type的值为00时有效,其值表示Error correction data的大小(字节),值为0010。 如果Error correction length type的值不为00,这个值设置为0000

?      Opaque Data Present:如果设置1,表示Error Correction Data后是opaque data。这个值应 该设为0.

?      Error correction length type:用于编码error correction data的大小。这个值应设置为0.

?      Error correction present:为1时,表示有error correction 信息。

?      Error correction data

ASF Payload parsing information

1、第一层,Header Object,这个结构一般不解析。没什么用。跳过这30字节的结构

2、第二层,File Properties Object 、Header ExtensionObject和Stream Properties Object,前两个也没解析,直接跳过了。Header Object是一个容器,它里面必须包含一个File PropertiesObject, 一个Header Extension Object 和至少一个Stream Properties Object。

a)  用while循环从读入的数据中查找每个Stream Properties Object类型的128bit的Object ID,(Stream Properties Object是流属性对象,定义一个媒体流和其属性),如果找到,返回找到的位置pos。ff_asf_stream_header(B7DC0791-A9B7-11CF-8EE6-00C00C205365(guid),16进制guid的顺序是9107DCB7-B7A9-CF11-8EE6-00C00C205365)这个标志代表了Stream Properties Object类型的Object ID。

b)  获取16字节的Stream Type字段,判断当前stream的类型(video、audio)

i.  如果当前stream是ASF_Video_Media型,即Stream Type是BC19EFC0-5B4D-11CF-A8FD-00805F5C442B(guid),16进制guid的顺序是C0EF19BC-4D5B-CF11-A8FD-00805F5C442B,那么进行Video media type类型的Type-Specific Data数据解析。转第三层,BITMAPINFOHEADER 类型结构。

ii.  如果当前stream是ASF_Audio_Media类型,即Stream Type是F8699E40-5B4D-11CF-A8FD-00805F5C442B(guid),16进制guid的顺序是409E69F8-4D5B-CF11-A8FD-00805F5C442B,那么进行Audio media type类型的Type-Specific Data数据解析。转第三层,WAVEFORMATEX类型结构。

iii.当前stream是ASF_Command_Media型,即Stream Type是59DACFC0-59E6-11D0-A3AC-00A0C90348F6(guid),16进制guid的顺序是C0CFDA59-E659-11D0-A3AC-00A0C90348F6。不做其他操作。

iv.  当前stream是ASF_Binary_Media型,即Stream Type是3AFB65E2-47EF-40F2-AC2C-70A90D71D343(guid),16进制guid的顺序是E265FB3A-EF47-F240-AC2C-70A90D71D343,转第三层,ASF_Binary_Media类型。

c)  获取Type-SpecificData Length ,4字节。用来解析第三层的Type-Specific Data数据。

d)  跳过ErrorCorrection Data Length,4字节字段

e)  紧接着的16bit分别为Stream Number(7)\Reserved(8bit,全为0)\Encrypted Content Flag(1bit,一般不设置,一旦设置,这个stream里得date就不解析),这16个bit与上0x7f之后,得到StreamNumber,即以后用到的video或者audio pid。

f)  跳过Reserved(4字节)字段,解析第三层的Type-Specific Data。

3、第三层,Type-Specific Data。

Type-Specific Data的数据类型由Stream Type字段限定。根据不同的类型,各自有不同的Type-Specific Data结构。

a)ASF_Binary_Media类型。

这个类型结构体的参数基本不解析,除了Major media type为31178C9D-03E1-4528-B582-3DF9DB22F503(guid),16进制guid的顺序是9D8C1731-E103-2845-B582-3DF9DB22F503的时候,类型为audio,会去解析这个结构体之后的参数。以ASF_Audio_Media方式解析。

Stream Type是ASF_Binary_Media类型的。那么这个stream包含:Stream Type是ASF_Binary_Media类  型的。那么这个stream包含:

Major mediatype,128bit;

Media subtype,128bit;

Fixed-size samples,32bit;

Temporalcompression,32bit;

Sample size,32bit;

Format type,128bit;

Format datasize,32bit;

b) Stream Type是ASF_Audio_Media类型的。那么这个stream使用WAVEFORMATEX 结构:

WAVEFORMATEX 定义音频数据格式

typedef struct

{

WORD wFormatTag;//数据格式 如果包含在WAVEFORMATEXTENSIBLE中则必须为WAVE_FORMAT_EXTENSIBLE

WORD nChannels;//通道 通常为单通道,stereo data使用双通道

DWORD nSamplesPerSec;//采样输入率 hz

DWORD nAvgBytesPerSec;

WORD nBlockAlign;

WORD wBitsPerSample;

WORD cbSize;

} WAVEFORMATEX;

//WAVEFORMATEX 为波形音频流格式的数据结构,包括

//wFormatTag(16bit)设置波形声音的格式,nChannels(16bit)设置音频文件的通道数量,对于单声道的声音,此此值为1。对于立体声,此值为2.

//nSamplesPerSec(32bit)   设置每个声道播放和记录时的样本频率。

// 如果wFormatTag = WAVE_FORMAT_PCM,那么nSamplesPerSec通常为8.0 kHz, 11.025 kHz, 22.05 kHz和44.1 kHz。例如对于采样率为11.025 kHz的音频,

//nSamplesPerSec 将被设为11025。对于非PCM格式的,请根据厂商的设定计算

//nAvgBytesPerSec(32bit)设置请求的平均数据传输率,单位byte/s。这个值对于创建缓冲大小是很有用的

//nBlockAlign(16bit),以字节为单位设置块对齐。块对齐是指最小数据的原子大小。

//如果wFormatTag = WAVE_FORMAT_PCM,nBlockAlign 为(nChannels*wBitsPerSample)/8。对于非PCM格式请根据厂商的说明计算。

//wBitsPerSample(16bit)根据wFormatTag的类型设置每个样本的位深(即每次采样样本的大小,以bit为单位)。

//如果wFormatTag = WAVE_FORMAT_PCM,此值应该设为8或16,对于非PCM格式,根据厂商的说明设置。

//一些压缩的架构不能设置此值,此时wBitsPerSample应该为零

//cbSize(16bit),额外信息的大小,以字节为单位,额外信息添加在WAVEFORMATEX结构的结尾。

//这个信息可以作为非PCM格式的wFormatTag额外属性,如果wFormatTag不需要额外的信息,

//此值必需为0,对于PCM格式此值被忽略。

根据第二层的Type-Specific Data Length参数,设置每个样本的位深(即每次采样样本的大小,以bit为单位)并且可以判断是否要解析第三层中的cbSize和额外信息,如果Type-Specific Data Length为14,每个样本的位深为8;其他情况下,位深未解析后面16bit的值得到的wBitsPerSample字段。如果Type-Specific DataLength大于等于18,表明后面有额外信息,需要获取cbSize(16bit),额外信息的大小,以字节为单位,额外信息添加在WAVEFORMATEX结构的结尾。额外信息应该叫WAVEFORMATEXTENSIBLE结构。参看第四层的WAVEFORMATEXTENSIBLE结构说明。

4、 第四层

4-1、WAVEFORMATEXTENSIBLE结构,存在于WAVEFORMATEX结构的末尾Codec Specific Data字段。专用于ASF_Audio_Media.WAVEFORMATEXTENSIBLE,为了解决多声道排序和高精度数据的问题,Microsoft 定义了一个新的结构WAVE_FORMAT_EXTENSIBLE。此新结构不仅仅能够解决这些问题,而且还提供了一个自我注册新数据格式的机制。SubFormat字段设置为指定WAVE_FORMAT_EXTENSIBLE结构所描述数据类型的 GUID。WAVEFORMATEXTENSIBLE结构描述了一个多通道波形格式。这个结构是对WAVEFORMATEX的扩展,配置了已经由 WAVEFORMATEX中cbSize成员支持的额外字节。当需要WAVEFORMATEX的地方,WAVEFORMATEXTENSIBLE结构能够被转化为WAVEFORMATEX。

typedef struct

{

WAVEFORMATEX  Format;

union

{

WORD wValidBitsPerSample;

WORD wSamplesPerBlock;

WORD wReserved;

}Samples;

DWORD dwChannelMask;

GUID  SubFormat;

} WAVEFORMATEXTENSIBLE;

通过wValidBitsPerSample和dwChannelMask字段可以获得编码方式码。通过编码方式码,可以确定一帧的数据大小和最终的编码方式。

4-2、BITMAPINFOHEADER 类型结构,专用于ASF_Video_Media.主要用来解析视频每一帧的宽高和每一像素的位深,还有编码方式与video_pid.

关于WMV格式文件的解析相关推荐

  1. python 实现文件的批量压缩为.zip格式+.zip格式文件的解析

    python 实现文件的批量压缩为.zip格式+.zip格式文件的解析 python 实现文件的批量压缩为.zip格式 Python解析.zip文件的常见函数 python 实现文件的批量压缩为.zi ...

  2. CentOS下如何用SMplayer播放WMV格式文件

    CentOS下如何用SMplayer播放WMV格式文件 原文:http://www.linuxidc.com/Linux/2016-05/131665.htm 安装的是CentOS 6.6,想用来做桌 ...

  3. Android之解决PC端上传http表单格式文件手机解析文件名乱码问题和PC浏览器下载文件的文件名显示乱码问题

    1 问题 问题1. 手机写socket作为服务器,PC浏览器上传http表单格式文件,然后手机端解析携带中文的文件名我解析是乱码. 问题2. 手机写了socket作为服务器,PC浏览器下载文件,但是浏 ...

  4. bam文件读取_SAM/BAM 格式文件内容解析

    一.首先需要知道以下几个知识点: 1.1-based coordinate system A coordinate system where the rst base of a sequence is ...

  5. VS2003版本Json格式文件的解析

    1.VS2003版本Jsoncpp环境配置 参考链接:https://blog.csdn.net/hao745580264_dawei/article/details/107475207 2.细节问题 ...

  6. WMV格式文件播放器

     <object id="player" height="244" width="346" classid="CLSID ...

  7. 【一周工作总结】循环调用接口,和一些Xml格式文件的解析。

    上周五,项目组老大,给我提了一下有个关于WebService的项目.由于当时时间仓促,对项目的需求理解出现偏差,导致我做到周二晚,才知道做的东西是方向反了.没办法,只好加加班,努力赶出来了. 1.既然 ...

  8. 将DAT格式视频文件转换成ASF和WMV格式视频文件

    以*.ASF和*.WMV为后缀名的视频文件,是微软针对RM格式视频文件而产生的,它们也是WindowsMedia的核心.它们的共同特点是采用MPEG-4压缩算法,所以压缩率和图像的质量都很不错(只比V ...

  9. html 直接播放wma,苹果Mac电脑怎么播放.wma和.wmv格式的文件?

    我们都知道,苹果Mac电脑自带的播放器QuickTime默认可以播放很多的格式.但是不能直接播放.wma和.wmv文件(这两个格式是windows媒体文件常用格式),所以这里我们就需要借助一些第三方的 ...

最新文章

  1. 【C#】类——里式转换
  2. 机器学习开源项目Top10
  3. shell特殊符号cut命令,sort_wc_uniq命令,tee_tr_split命令
  4. 一位老司机谈谈掏心窝子的话
  5. 设计模式复习-访问者模式
  6. 再不用担心DataRow类型转换和空值了(使用扩展方法解决高频问题)
  7. 物联网技术概论的课程编号_选课推荐 | 第5期:物联网技术概论
  8. 用CubeSLAM跑自己的数据集
  9. Attention机制的小理解
  10. 0538-实战将lnmp服务中的上传目录独立分离到nfs服务器
  11. bgp状态idle什么原因_27-高级路由:BGP状态
  12. mysql5.5忘记密码重置方法_mysql5.5忘记密码重置方法
  13. CodeForces-831A-Unimodal Array (水题)
  14. android微信认证失败怎么办,微信登陆好友头像验证失败该怎么办?
  15. Android 左右滑动控件
  16. 《软技能--代码之外的生存指南》学习笔记之自我营销篇
  17. spark-sql-perf
  18. 如何使用JavaScript,纯前端实现字符、字数统计?
  19. .php文件是病毒吗,php病毒
  20. 国内UPS电源品牌排行-科士达UPS

热门文章

  1. hard code代码的意思
  2. atlas单机模式代码_已经厌倦了海盗间的纷争?海盗游戏《ATLAS》的单机模式了解一下...
  3. iOS app上架规则
  4. 七牛云人脸比检测及人脸比对
  5. 有限元方法基础入门教程(一维弹性问题的有限元程序)
  6. 用计算机操纵Photoshop快速做事
  7. 拉卡拉智能POS这些功能,你最认可哪个?
  8. HDBaseT解决当务之急!
  9. App Key 和 App Secret 有什么用?
  10. 【Linux下】 线程同步 生产者与消费者模型