基于Delphi的DirectShow开发概述1.doc_第1页
基于Delphi的DirectShow开发概述1.doc_第2页
基于Delphi的DirectShow开发概述1.doc_第3页
基于Delphi的DirectShow开发概述1.doc_第4页
基于Delphi的DirectShow开发概述1.doc_第5页
已阅读5页,还剩16页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

基于Delphi的DirectShow开发概述1.txt26选择自信,就是选择豁达坦然,就是选择在名利面前岿然不动,就是选择在势力面前昂首挺胸,撑开自信的帆破流向前,展示搏击的风采。第一部分:背景知识DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布。它经过DirectX 6.0中的DirectX Media发展而来,集成了DirectX家族中的其他成员(DirectDraw、DirectSound等),可以说是一位“集大成者”。 DirectShow能做些什么? DirectShow为多媒体流的捕捉和回放提供了强有力的支持。运用DirectShow,可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。它广泛地支持各种媒体格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒体数据的回放变得轻而易举。另外,DirectShow直接支持DVD的播放,视频的非线性编辑,以及与数字摄像机的数据交换。更值得一提的是,DirectShow提供的是一种开放式的开发环境,每个功能模块都采取COM组件方式,称为Filter,开发者也可以开发自己的功能Filter来扩展DirectShow的应用。按照功能来划分,Filter分为3类:Source Filter, Transform Filter, Rendering Filter。前者负责获取数据,数据源可以是文件、数字摄像机等,然后将数据往下传输;中间者负责数据的格式转换,比如数据流的分离与合成、编码解码等,然后把数据继续往下传输;后者负责数据的去向给声卡、显卡进行播放或者输出到文件存储。第二部分核心技术DirectShow的开发实际就是Filter的开发,DirectShow自身提供了,下面就是Filter概述。1、DirectShow的 FilterFilter 一般分为下面几种类型。(1)源过滤器(source filter):源过滤器引入数据到过滤器图表中,数据来源可以是文件、网络、照相机等。不同的源过滤器处理不同类型的数据源。(2)变换过滤器(transform filter):变换过滤器的工作是获取输入流,处理数据,并生成输出流。变换过滤器对数据的处理包括编解码、格式转换、压缩解压缩等。 (3)提交过滤器(renderer filter):提交过滤器在过滤器图表里处于最后一级,它们接收数据并把数据提交给外设。(4)分割过滤器(splitter filter):分割过滤器把输入流分割成多个输出。例如,AVI分割过滤器把一个AVI格式的字节流分割成视频流和音频流。(5)混合过滤器(mux filter):混合过滤器把多个输入组合成一个单独的数据流。例如,AVI混合过滤器把视频流和音频流合成一个AVI格式的字节流。过滤器的这些分类并不是绝对的,例如一个ASF读过滤器(ASF Reader filter)既是一个源过滤器又是一个分割过滤器。2、关于Filter Graph ManagerFilter Graph Manager也是一个com对象,用来控制Filter graph中的所有的filter,主要有以下的功能: 1) 用来协调filter之间的状态改变,从而使graph 中的所有的filter的状态的改变应该一致。2) 建立一个参考时钟。3) 将filter 的消息通知返回给应用程序4) 提供用来建立 filter graph的方法。 简单描述,Graph就是各个Filter组成的一个流程图。 SourceFilter-|-SpliterFilter-(Video-pin)-TransFormFilter-VideoRender |-(Audio-pin)-ACMWraperFilter-DirectSoundFilter程序启动过程,先创建各个filter的com对象,然后使用FilterGraph.Addfilter加入到Graph中,然后把每个Filter按照数据流把OutPin和inpuin连接起来。最好启动FilterGraph.play即可。Directshow是基于模块化,每个功能模块都采取COM组件方式,称为Filter。Directshow提供了一系列的标准的模块可用于应用开发,开发者也可以开发自己的功能Filter来扩展Directshow的应用。下面我们用一个例子来说明如何采取Filter来播放一个AVI的视频文件。1) 首先从一个文件中读取AVI数据,形成字节流。(这个工作由源Filter完成)2) 检查AVI数据流的头格式,然后通过AVI分割Filter将视频流和音频流分开。3) 解码视频流,根据压缩格式的不同,选取不同的decoder filters 。4) 通过Renderer Filter重画视频图像。5) 音频流送到声卡进行播放,一般采用缺省的 DirectSound Device Filter。 状态改变,Graph中的filter的状态改变应该一致,因此,应用程序并将状态改变的命令直接发给filter,而是将相应的状态改变的命令发送给Filter graph Manager,由manager将命令分发给graph中每一个filter。Seeking也是同样的方式工作,首先由应用程序将seek命令发送到filter graph 管理器,然后由其分发给每个filter。参考时钟,graph中的filter都采用的同一个时钟,称为参考时钟(reference clock),参考时钟可以确保所有的数据流同步,视频桢或者音频桢应该被提交的时间称为presentation time.presentation time 是相对于参考时钟来确定的。Filter graph Manager应该选择一个参考时钟,可以选择声卡上的时钟,也可以选择系统时钟。Graph事件, Graph 管理器采用事件机制将graph中发生的事件通知给应用程序,这个机制类似于windows的消息循环机制。Graph构建的方法,graph管理器给应用程序提供了将filter添加进graph的方法,连接filter的方法,断开filter连接的方法。但是,graph 管理器没有提供如何将数据从一个filter发送到另一个filter的方法,这个工作是由filter在内部通过pin来独立完成的。3、媒体类型因为Directshow是基于com组件的,就需要有一种方式来描述filter graph每一个点的数据格式,例如,我们还以播放AVI文件为例,数据以RIFF块的形式进入graph中,然后被分割成视频和音频流,视频流有一系列的压缩的视频桢组成,解压后,视频流由一系列的无压缩的位图组成,音频流也要走同样的步骤。媒体类型是一种很普遍的,可以扩展的用来描述数字媒体格式的方法,当两个filter连接的时候,他们会就采用某一种媒体类型达成一致的协议。媒体类型定义了处于源头的filter将要给下游的filter发送什么样的数据,以及数据的physical layout。如果两个filter不能够支持同一种的媒体类型,那么他们就没法连接起来。对于大多数的应用来说,也许你不用考虑媒体类型,但是,有些应用程序中,你会直接应用到媒体类型的。媒体类型是通过AM_MEDIA_TYPE结构定义的。Filters通过pin的连接来传递数据,数据流是从一个filter的输出pin流向相连的filter的输入pin。输出pin常用的传递数据的方式是调用输入pin上的IMemInputPin:Receive方法。对于filter来说,可以有好几种方式来分配媒体数据使用的内存块,可以在堆上分配,可以在DirectDraw的表面,也可以采用GDI共享内存,还有其他的一些方法,在Directshow中用来进行内存分配任务的是内存分配器(allocator),也是一个COM对象,暴露了一个IMemAllocator接口。当两个pin连接的时候,必须有一个pin提供一个allocator,Directshow定义了一系列函数调用用来确定由哪个pin提供allocator,以及buffer的数量和大小。在数据流开始之前,allocator会创建一个内存池(pool of buffer),在开始发送数据流以后,源filter就会将数据填充到内存池中一个空闲的buffer中,然后传递给下面的filter。但是,源filter并不是直接将内存buffer的指针直接传递给下游的filter,而是通过一个media samples的COM对象,这个sample是allocator创建的用来管理内存buffer。Media sample暴露了IMediaSample接口,一个sample包含了下面的内容:一个指向没有发送的内存的指针。一个时间戳一些标志媒体类型。时间戳表明了presentation time,Renderer filter就是根据这个时间来安排render顺序的。标志是用来标示数据是否中断等等,媒体类型提供了中途改变数据格式的一种方法,不过,一般sample没有媒体类型,表明它们的媒体类型一直没有改变。当一个filter正在使用buffer,它就会保持一个sample的引用计数,allocator通过sample的引用计数用来确定是否可以重新使用一个buffer。这样就防止了buffer的使用冲突,当所有的filter都释放了对sample的引用,sample才返回到allocator的内存池,供重新使用基于Delphi的DirectShow开发概述2MajorType:主要类型;例如视频,音频,还是位流subType : 辅助说明类型,例如视频中的YUV12,还是UYVY等等formatType: 格式描述,更为细节的结构体。例如,视频大小,频率,帧率等, 可以使用FORMAT_VIDEOINFO(VIDEOINFOHEADER),FORMAT_WAVEFORMATEX(WAVEFORMATEX)结构体来描述/PAMMediaType当使用AM_MEDIA_TYPE数据结构来描述媒体类型的时候,如果MajorType,subType,formatType都指定了GUID,那么这就是完全媒体类型。 *Filter的连接* Filter的连接实际上就是Pin的连接。连接方向总是由上一级的Filter(UpStream Filter)的输出Pin指向下一级Filter(DownStream Filter)的输入Pin。1.Filter连接过程 Pin也是一种COM接口。实现了IPIN的接口。一般通过调用(下面的函数来连接): IFilterGraph.ConnectDirect,IGraphBuilder.Connect,IGraphBuilder.Render,IGraphBuilder.RenderFile 下面就是个范例,一般Filter是在停止状态下连接的。 /连接 source - MPEG1Spliter Source.FindPin(StringToOLEStr(Output), OutPin); MPEG1Splitter.FindPin(StringToOLEStr(Input), inPin); hr := (FilterGraph1 as IGraphBuilder).Connect(OutPin, InPin); if FAILED(hr) then begin ShowMessage(Failed connect mpg Source - MPEG1Splitter); exit; end; 2.FilterGraph构建的方法 1)IFilterGraph.AddFilter 该参数提供一个Filter对象,将其加入到FilterGraph中. 2)IFilterGraph.ConnectDirect 该参数提供输出Pin,输入Pin以及媒体类型,进行直接连接 3)IGraphBuilder.AddSourceFilter 该参数提供源文件名,自动将一个SourceFilter加载到FilterGraph中 4)IGraphBuilder.Connect 该参数提供输出Pin,输入Pin以及媒体类型,进行连接,如果失败,自动尝试在中间加入必要的格式转换Filter 5)IGraphBuilder.Render 该参数提供输出Pin,自动间加入必要的Filter完成剩下的部分FilterGraph的构建(直到连到RenderFilter上) 6)IGraphBuilder.Render 该参数提供源文件名,自动间加入必要的Filter完成这个文件的回放 /下面范例,表示该FilterGraph中有6个Filter,他们都是由COM对象创建而来。 var Source : IBaseFilter; MPEG1Splitter : IBaseFilter; MpegVcodec : IBaseFilter; AviDec : IBaseFilter; AviDest : IBaseFilter; Writer : IBaseFilter; hr : HRESULT; OutPin, InPin : IPin; begin CoCreateInstance(CLSID_AsyncReader, nil, CLSCTX_INPROC,IID_IBaseFilter, Source); /典型的拉模式 CoCreateInstance(CLSID_MPEG1Splitter, nil, CLSCTX_INPROC,IID_IBaseFilter, MPEG1Splitter); /MPEG1格式 CoCreateInstance(CLSID_CMpegVideoCodec, nil, CLSCTX_INPROC,IID_IBaseFilter, MpegVcodec); /MPEG编码 CoCreateInstance(CLSID_AVIDec, nil, CLSCTX_INPROC,IID_IBaseFilter, AviDec); /AVI解码 CoCreateInstance(CLSID_AviDest, nil, CLSCTX_INPROC, IID_IBaseFilter, AviDest); /AVI目标 CoCreateInstance(CLSID_FileWriter, nil, CLSCTX_INPROC,IID_IBaseFilter, Writer); /写文件 (FilterGraph1 as IFilterGraph).AddFilter(Source, Source); (FilterGraph1 as IFilterGraph).AddFilter(MPEG1Splitter, MPEG1Splitter); (FilterGraph1 as IFilterGraph).AddFilter(MpegVcodec, MpegVcodec); (FilterGraph1 as IFilterGraph).AddFilter(AviDec, AviDec); (FilterGraph1 as IFilterGraph).AddFilter(AviDest, AviDest); (FilterGraph1 as IFilterGraph).AddFilter(Writer, Writer); end; 3. 一般使用GraphEdit可以查看到目前正常安装在系统中的Filter,如果是安装在DirectShow目录下的可以通过指定CLSID 用CoCreateInstance来创建。在其它目录下的,必须通过系统枚举来创建。 系统提供了一个CLSID_SystemDeviceEnum,用CoCreateInstance创建,并获取ICreateDevEnum接口。然后 使用ICreateDevEnum.CreateClassEnumerator为指定的类型目录创建一个枚举器,并获得IEnumMoniker接口。 使用IEnumMoniker.next方法,媒体该目录下所有可用设备标识(Device Moniker),每个设备标识对象上都实现了Imoniker接口 调用Imoniker.bindtoStorage之后就可以访问设备标识属性集。比如得到设备的显示名字。 调用Imoniker.BindToObject可以将设备标识绑定成一个DirecshowFilter,然后调用IFilterGraph.addFilter加入FilterGraph 参加工作。 DirectShow通常有两个名字:显示名字例如:device:cm:33D9A760-90C8-11D0-BD43-00A0C911CE86xvid 友好名字例如:xvid mpeg4 decoder 下面就是调用代码 var i, j: integer; AMoniker, MyMoniker: IMoniker; PropBag: IPropertyBag; AVariant: OleVariant; CLSID: TGUID; Found: boolean; begin for i := 0 to SysDevEnum.CountCategories - 1 do cbCategories.Items.Add(SysDevEnum.Categoriesi.FriendlyName); /SysDevEnum:TSysDevEnum; Found := false; j := 0; MyMoniker := Filter.BaseFilter.Moniker; if MyMoniker = nil then exit; MyMoniker.BindToStorage(nil,nil,IPropertyBag, PropBag); if PropBag.Read(CLSID,AVariant,nil) = S_OK then CLSID := StringToGUID(AVariant) else CLSID := GUID_NULL; for i := 0 to SysDevEnum.CountCategories - 1 do begin SysDevEnum.SelectIndexCategory(i); if SysDevEnum.CountFilters 0 then for j := 0 to SysDevEnum.CountFilters - 1 do begin if IsEqualGUID(CLSID, SysDevEnum.Filtersj.CLSID) then begin AMoniker := SysDevEnum.GetMoniker(j); Found := AMoniker.IsEqual(MyMoniker) = S_OK; AMoniker := nil; end; if Found then Break; end; if Found then begin cbCategories.ItemIndex := i; cbCategoriesChange(nil); lbFilters.ItemIndex := j; lbFiltersClick(nil); break; end; end; PropBag := nil; MyMoniker := nil; PAMMediaType = TAMMediaType; _AMMediaType = record majortype : TGUID; subtype : TGUID; bFixedSizeSamples : BOOL; bTemporalCompression : BOOL; lSampleSize : ULONG; formattype : TGUID; pUnk : IUnknown; cbFormat : ULONG; pbFormat : Pointer; end; Filter开发基础-基类分析 1)TBCBaseFilter TBCBaseFilter = class(TBCUnknown, IBaseFilter, IAMovieSetup) 是最基本Filter的基类,使用方法: (1)声明一个新类继承自 TBCBaseFilter (2)在新类中定义一个Filter上Pin的实例。(Pin从TBCBasePin继承) (3)实现纯虚函数TBCBaseFilter.GetPin,用于返回Filter上各个Pin的对象指针 (4)实现纯虚函数TBCBaseFilter.GetPinCount,用于返回Filter上Pin的数量 (5)考虑如何处理从输入Pin进来的Sample数据 2)TBCBasePin TBCBasePin实现了PIn接口,TBCBasePin设计了Pin的整个连接过程。也实现了IQualityControl质量控制接口。 在TBCBasePin上实现了3个成员函数与Filter状态对应。 (1) Filter.Stopped TBCBasePin.Inactive (2) Filter.Spaused TBCBasePin.active (3) Filter.Running TBCBasePin.Run 在实际开发Filter时,有可能重写该3个函数,用来初始化和释放必要资源。实现方法: (1)从TBCBasePin派生一个子类 (2)实现纯虚函数TBCBasePin.CheckMediaType,进行Pin连接时媒体类型检测 (3)实现纯虚函数TBCBasePin.GetMediaType,提供Pin上的首先媒体类型 (4)实现Ipin.BeginFlush和IPin.EndFlush两个函数 (5)可能需要重写的函数包括。TBCBasePin.Inactive,TBCBasePin.active,TBCBasePin.Run, TBCBasePin.CheckConnect(连接的时候检查,如查询对方Pin上是否支持某个特殊接口), TBCBasePin.BreakConnect(断开连接,并进行必要的资源释放), TBCBasePin.CompleteConnect(完成连接时被调用,可以在这个函数中获得当前连接用的媒体类型等参数), TBCBasePin.EndOfStream(当上流数据全部传送完毕后被调用,如果这是个TransformFilter则将继续往下送, 如是个RenderFilter,则需要向FilterGraph发送一个EC_COMPLETE事件) TBCBasePin.Notify(直接响应质量控制,或者将质量控制消息往上一级Filter发送) 3)TBCBaseInputPin和TBCBaseOutputPin TBCBaseInputPin和TBCBaseOutputPin都是从TBCBasePin派生而来, TBCBaseInputPin实现了ImeminputPin(用于推模式的数据传送) TBCBaseOutputPin主要完成了传送数据所用的Sample管理器(Allocate)的协商,并重写了TBCBasePin.active(用于 实际的Sample内存分配),TBCBasePin.inactvie(用于Sample内存的释放)。 TBCBaseInputPin使用方法(派生一个子类,并且至少重写如下函数): (1) TBCBaseInputPin.BeginFlush (1) TBCBaseInputPin.EndFlush (1) TBCBaseInputPin.Receive (1) TBCBaseInputPin.CheckMediaType(一般在输出Pin上实现该函数) (1) TBCBaseInputPin.GetMediaType TBCBaseOutputPin使用方法(派生一个子类,并且至少重写如下函数): (1)重写TBCBasePin.CheckMediaType进行连接时的媒体类型检查 (2)实现纯虚函数TBCBaseOutputPin.DecideBufferSize,决定Sample内存的大小 (3)重写纯虚函数TBCBasePin.GetMediaType,提供Pin上的首先媒体类型 4)TBCMediaType TBCMediaType用于数据传输的Sample的实现类,TBCMediaType实现了IMediaSample2的接口,TBCMediaType封装了 一个指向一块内存的指针,通过TBCMediaType.GetPointer得到 5)TBCSourceStream = class(TBCBaseOutputPin) 提供了“推”(Push)的能力,实现了一个线程(TBCAMThread),Sample数据就是靠这个线程向下一级Filter发送的。 实现方法: (1)从TBCSourceStream派生一个子类作为Pin (2)实现纯虚函数TBCSourceStream.CheckMediaType,进行Pin连接时媒体类型检测 (3)实现纯虚函数TBCSourceStream.GetMediaType,提供Pin上的首先媒体类型 (4)实现TBCBaseOutputPin.DecideBufferSize,决定Sample内存大小,在Pin连接时会执行 (5)实现TBCSourceStream.FillBuffer,为即将传送出去的Sample填充数据 (6)可选地实现TBCSourceStream.OnThreadCreate, TBCSourceStream.OnThreadDestroy, TBCSourceStream.OnThreadStartPlay 等函数,进行适当时机的初始化,资源管理等操作。 6)TBCTransformFilter 实现了媒体类型的转换,主要实现如下函数: (1)TBCTransformFilter.CheckInputType (2)TBCTransformFilter.CheckTransForm (3)TBCTransformFilter.DecideBufferSize (4)TBCTransformFilter.GetMediaType (5)TBCTransformFilter.TransForm 7)TBCTransinPlaceFilter 是一个“就地”处理的转换Filter。 8)TBCVideoTransformFilter 一个视频质量控制的基类。通过输入Pin上的Receive函数接收Sample时,能够根据质量消息决定是否丢帧,这个类 主要是为开发AVI解码Filter而设计的。使用方法基本和TBCTransformFilter相同。 9)TBCBaseRenderer 默认实现使用TBCRenderInutPin类的输入Pin。还实现了IMediaSeeking和IMediaPosition接口。使用方法: (1)实现TBCBaseRenderer.CheckMediaType,用于检查输入Pin连接用的媒体类型 (2)实现TBCBaseRenderer.DoRenderSample,处理当前的Sample 如果我们不处理Sample,需要写文件,基类可以选择从TBaseFilter,而此时输入Pin最好选择从TBCRenderInputIn 派生类。 10)TBCBaseVideoRenderer 实现了VideoFilter基类,其中实现了IQualityControl用于视频质量控制,IQualProp用于在Filter属性页显示一些 实时性能参数。使用方法与TBCBaseRenderer相同。基于Delphi的DirectShow开发概述3 Delphi设计Directshow其实也是比较简单的,看了前面两个概述,相信你也会明白一些了,问题就是你要有什么杨的需求,然后根据需求来选择合适的Filter基类,从基类(BassFilter)继承下来后,只需要覆盖指定的函数就可以了。 例如我们要设计一个屏幕(Desktop)捕获的的Filter,每秒捕获大概10帧(10副抓屏bitmap),这个明显需要实现Push下推的功能,因此我们可以选择从TBCSourceStream基类派生一个子类,然后实现如下几个函数即可: /GetMediaType函数是下级Filter在和本Filter连接时在下级InpuPin接口上调用的 function GetMediaType(iPosition: Integer; out MediaType: PAMMediaType): HResult; override; /实现虚函数CheckMediaType,实现接受8, 16, 24 or 32 RGB位视频格式 /如果媒体格式不能接受,则返回E_INVALIDARG function CheckMediaType(MediaType: PAMMediaType): HResult; override; /在本Filter的OutPutPin接口和下级InputPin接口协商时调用的,主要用来协商每个Sample的大小 function DecideBufferSize(Allocator: IMemAllocator;Properties: PAllocatorProperties): HRESULT; override; /设置视频媒体参数,在初始化Filter时调用 function SetMediaType(MediaType: PAMMediaType): HRESULT; override; /以上几个函数是在和下级Filter进行接口连接和协商时调用,FillBuffer函数则是由本Filter内置线程按一定时间 /间隔调用,这里当然是把抓屏的Bitmap数据填充到Sample中,推给下一级。 function FillBuffer(Sample: IMediaSample): HResult; override; /实现虚函数,质量控制功能 function Notify(Filter: IBaseFilter; q: TQuality): HRESULT; override; stdcall; 具体代码分析如下: CLSID_PushSourceDesktop: TGUID = 570757C1-D2D8-42D1-BA0C-24E1BED3F62F; /PushFilter注册名 /Pin注册类型结构 TRegPinTypes = record clsMajorType: PGUID; clsMinorType: PGUID; end; /Setup信息结构 sudPinTypes: TRegPinTypes = ( /视频流的类型 clsMajorType: MEDIATYPE_Video; /所用可用类型 clsMinorType: MEDIASUBTYPE_NULL ); /Filter注册Pin接口信息结构 TRegFilterPins = record strName : PWideChar; bRendered : BOOL; bOutput : BOOL; bZero : BOOL; bMany : BOOL; oFilter : PGUID; strConnectsToPin : PWideChar; nMediaTypes : LongWord; lpMediaType : PRegPinTypes; end; /定义实例 sudOutputPinDesktop: array0.0 of TRegFilterPins = ( ( strName: Output; / Pin名称 bRendered: FALSE; /是否是Render bOutput: TRUE; /是否是输出接口 bZero: FALSE; /是否允许为0 bMany: FALSE; /是否有更多 oFilter: nil; /连接的Filter strConnectsToPin: nil; /连接的Pin nMediaTypes: 1; /支持类型数量 lpMediaType: sudPinTypes / Pin信息 ) ); DefaultFrameLength: TReferenceTime = FPS_10; /由参考时钟确定FPS_10=1000000 PushDesktopName: WideString = _ PushSource Desktop Filter; /PushFilter友好名 /Pin接口类封装,继承TBCSourceStream-TBCBaseOutputPin-TBCBasePin(TBCUnknown, IPin, IQualityControl) TBCPushPinDesktop = class(TBCSourceStream) protected FFramesWritten : Integer; /在播放

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论