基于流媒体的数字电视的软件_第1页
基于流媒体的数字电视的软件_第2页
基于流媒体的数字电视的软件_第3页
基于流媒体的数字电视的软件_第4页
基于流媒体的数字电视的软件_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

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

文档简介

基于流媒体的数字电视的软件实现——关于TS流的解析

制作人:张进目录1.MPEG系统2.TS的基本概念3.TS的解析4..TS流的数据结构5.TS流的解析实例讲解1.MPEG系统随着通信数字技术的快速发展,一种全新的采用数字电视技术的电视系统被广泛采用,相比模拟电视系统其具有很多优点。数字电视的核心技术是MPEG(MovingPictureExpertGroup)的编码与解码技术,基于它的标准产生两种码流:节目码流和传输码流(TS流)。MPEG系统任务简介一.MPEG–2是一个系统,它的作用是:对音频、视频、数据、控制等基本比特流实现复用提供各种定时及初始化经解码器提供PSI(节目特定信息)支持有条件接收(CA),随机接入,数字存储和纠错二.MPEG-2系统输出两个数码流:节目比特流PS,用于相对无误差的环境传输比特流TS,用于有噪声媒质MPEG系统结构图系统层信息流通方式

首先,从视频、音频编码器而来的数据称为基本码流(ES),然后,ES被分别打包,形成两个打包的基本码流(PES),再后,PES被复接成一个节目码流(PS)和一个传输码流(TS)。其图示如下基本码流ES→打包基本码流PES→节目码流PS传输码流TS

ES-ElementaryStreams(原始流),对视频、音频信号及其他数据进行编码压缩后的数据流称为原始流。原始流包括访问单元,比如视频原始流的访问单元就是一副图像的编码数据。

PES-PacketizedElementaryStreams(分组的原始流),原始流形成的分组称为PES分组,是用来传递原始流的一种数据结构

节目是节目元素的集合。节目元素可能是原始流,这些原始流有共同的时间基点,用来做同步显示。

PS的组成单位是PES分组。TS用来传输和保存多道节目的编码数据或其他数据,TS的组成单位是节目。PS适用于不容易发生错误的环境,以及涉及到软件处理的应用,典型应用如DVD光盘的文件存储TS适用于容易发生错误的环境,典型应用就是数字电视信号的传输。TS和PS是可以互相转换的,比如从TS中抽取一道节目的内容并产生有效的PS是可能。

MPEG-2的同步

MPEG算法提供一定的定时方法,保证视音频的同步。

MPEG-1为解码器制定了两个时钟:系统时钟基准(SCR)显示时间标记(PTS)

MPEG-2为解码器制定了三个时钟:系统时钟基准(SCR)显示时间标记(PTS)节目时钟基准(PCR)2.TS的基本概念TransportStream传输流将具有共同时间基准或具有独立时间基准的一个或多个节目组合而成的单一的数据流传输流由一系列的包分组构成,分个分组是188个字节,其主体结构分为Header和Payload两部分。在Header部分是一些包的控制信息,其中PID字段是该包的身份,对包的操作的标识。在Payload字段中含有有效地负载或是传输字段。特点:包长固定,188字节;面向数字化分配媒介(有线、卫星、地面网)的传输层接口传输流由一道或多道节目组成,每道节目由一个或多个原始流和其他一些流复合在一起,包括视频流、音频流、节目特殊信息流(PSI)和其他数据包。其中,PSI有四种类型:程序关联表PAT、程序映射表PMT、网络信息表NIT、条件访问表CAT;TS包结构

PSI全称ProgramSpecificInformation,意为节目专用信息。传输流中是多路节目复用的,那么,怎么知道这些节目在传输流中的位置,区分属于不同节目呢?所以就还需要一些附加信息,这就是PSI。PSI也是插入到TS分组中的,它们的PID是特定值。MPEG-2中规定了4个PSI,包括PAT(节目关联表),CAT(条件访问表),PMT(节目映射表),NIT(网络信息表),这些PSI包含了进行多路解调和显示节目的必要的和足够的信息。应用中可能包括更多的信息,比如DVB-T中定义了SDT(服务描述表),EIT(环境信息表),BAT(节目组相关表),TDT(时间日期表)等,统称为DVB-SI(服务信息)。PSI的PID是特定的,含PSI的数据包必须周期性的出现在传输流中。

PMT(ProgramMapTable)节目映射表PMT所在分组的PID由PAT指定,所以要先解出PAT,再解PMT。PMT中包含了属于同一节目的视频、音频和数据原始流的PID。找到了PMT,解多路复用器就可找到一道节目对应的每个原始流的PID,再根据原始流PID,去获取原始流。

PAT(ProgramAssociationTable)节目关联表PAT所在分组的PID=0PAT中列出了传输流中存在的节目流PAT指定了传输流中每个节目对应PMT所在分组的PIDPAT的第一条数据指定了NIT所在分组的PID,其他数据指定了PMT所在分组的PID。

CAT(ConditionalAccessTable)条件访问表CAT所在分组的PID=1CAT中列出了条件控制信息(ECM)和条件管理信息(EMM)所在分组的PID。CAT用于节目的加密和解密NIT(NetworkInformationTable)网络信息表NIT所在分组的PID由PAT指定NIT提供一组传输流的相关信息,以及于网络自身特性相关的信息,比如网络名称,传输参数(如频率,调制方式等)。NIT一般是解码器内部使用的数据,当然也可以做为EPG的一个显示数据提供给用户做为参考。几种PSI之间的关系,如下图所示:首先PAT中指定了传输流中所存在的节目,及每个节目对应的PMT的PID号。比如Program1对应的PMT的PID=22,然后找到PID=22的TS分组,解出PMT,得到这个节目中包含的原始流的PID,再根据原始流的PID去找相应的TS分组,获取原始流的数据,然后就可以送入解码器解码了。3.TS流的解析PSI由四张表构成:PAT,PMT,CAT和NIT,这四张表分别描述了一个TS所包括的所有ES流的传输结构。首先的一个概念是,TS是以包形式传播,在编解码端都需要以一定的包ID来标识TS流里承载的内容,比如,PAT表会存在于一个或多个TS包里,所以要用一个特别的包ID来表示,另外,不同的ES流也需要不同的包ID来标识。我们有了PAT和PMT这两种表,解码器就可以根据PID,将TS上从不同ES来的TS包区分出来进行解码。TS的解码分两步进行,其一,是从PID为0的TS包里,解析出PAT表,然后从PAT表里找到各个节目源的PID,一般此类节目源都由若干个ES流组成,并描述在PMT表里面,然后通过节目源的PID,就可以在PMT表里检索到各个ES的PID。其二,解码器根据PMT表里的ES流的PID,将TS流上的包进行区分,并按不同的ES流进行解码。所以,TS是经过节目复用和传输复用两层完成的,即在节目复用时,加入了PMT,在传输复用时,加入了PAT。同样在节目解复用时,可以得到PMT,在传输解复用时,可以得到PAT。下图很好地概述了其思想。TS是支持多路复用的,所以它可用来传输经复用后的多层节目。在复用过程中,要注意的是,解码过程中所需要面对的时间参考和同步问题,因为解复用是需要各种信息同步进行的,所以在复用过程中,就需要插入相关的时间信息:PTS,DTS,PCR。在TS形成过程中,PTS和DTS是在ES打包成PES时,根据STC的参考,将其时钟信息注入PES包中的,而之后在PES切成TS时,再将PID和PCR信息注入到TS包中,当多路TS再进行复用的时候,各路TS的PCR将会被提取出来,再进行分析,然后再根据统一的STC参考,将新的PCR生成并注入到TS中去,最后,因为原来PAT表信息不在适用,所以新的PAT表需要再生成,并附加到新的TS流中去。经过这多层的复用之后,新的TS流即可以进入调制,传输阶段。过程可参见下图:解码过程要面对的问题是:解复用,视音频的同步,解码缓存器无上下溢。解复用即是将TS在同一信道里不同时序进行传输的节目分离出来;视音频同步由DTS,PTS和PCR三者协调完成,并且PCR是重建系统时间基准的绝对时标,而DTS和PTS是解码和重现时刻的相对时标;对解码缓存器无上下溢的问题,必须借助于系统目标解码器(STD)模型来对其进行实现,基本思想如下:TS流进入解码器后,首先由换向器,按照一定的时序关系,将各种ES流分解出来(其中也包括PSI信息流)。分解过后的ES流会进入各自的传输缓存器,通过之后,其PES流进入各自的主存储器,注意的是:PSI信息流会进入系统缓存器,最后也到达主存储器。最后,解码器根据DTS信息,从各个主存储器分别提取媒体或系统信息,进行解码,并根据PTS信息,将媒体内容进行显示处理。其过程可参见下图由图可知:1.PSI依然是"根",只有定位到PSI表后,才能索引出PAT,和各个节目的PMT表。2.PAT表记录了什么呢?表头信息和节目信息。

表头信息包含若干字段,但最关键的是version_number,why?因为TS流里任何节目信息的变化,这个字段都会往上递加,从而使得TS解码器可以据此让Decoder对不同节目做解码复位。

节目信息包括TS流里每路节目的program_number,PMT_PID。由此再结合表头信息,TS解码器即可以定位到具体的有变化的节目流(新增或减少)

3.接下来和PAT表平行的就是PMT表了,因为PAT表已经给出明确的节目信息,所以,解码器可以定位到自己需要的节目。在PMT表里,我们可以看到表头信息,和音视频的stream_type,PID信息等。这样解码器即可以定位到具体的音视频包上,进行节目解码。4.TS的数据结构因为在TS流里可以填入很多种东西,所以有必要有一种机制来确定怎么来标识这些数据。制定TS流标准的机构就规定了一些数据结构来定义。比如:PSI(ProgramSpecificInformation)表,所以解析起来就像这样:先接收一个负载里为PAT的数据包,在整个数据包里找到一个PMT包的ID。然后再接收一个含有PMT的数据包,在这个数据包里找到有关填入数据类型的ID。之后就在接收到的TS包里找含有这个ID的负载内容,这个内容就是填入的信息。根据填入的数据类型的ID的不同,在TS流复合多种信息是可行的。关键就是找到标识的ID号。数据结构(1)TS分组前面提到,TS分组由188个字节构成,其结构如下:

transport_packet(){

sync_byte

//8

transport_error_indicator

//1

payload_unit_start_indicator

//1

transport_priority

//1

PID

//13

transport_scrambling_control

//2

adaptation_field_control

//2

continuity_counter

//4

if(adaptation_field_control=='10'

||adaptation_field_control=='11'){

adaptation_field()

}

if(adaptation_field_control=='01'||adaptation_field_control=='11'){

for(i=0;i<N;i++){

data_byte

//8

}

}

}前面32bit的数据即TS分组首部,它指出了这个分组的属性。sync_byte

同步字节,固定为0x47

,表示后面的是一个TS分组,当然,后面包中的数据是不会出现0x47的transport_error_indicator

传输错误标志位,一般传输错误的话就不会处理这个包了payload_unit_start_indicator

这个位功能有点复杂,字面意思是有效负载的开始标志,根据后面有效负载的内容不同功能也不同,后面用到的时候再说。transport_priority

传输优先级位,1表示高优先级,传输机制可能用到,解码好像用不着。PID

这个比较重要,指出了这个包的有效负载数据的类型,告诉我们这个包传输的是什么内容。前面已经叙述过。transport_scrambling_control

加密标志位,表示TS分组有效负载的加密模式。TS分组首部(也就是前面这32bit)是不应被加密的,00表示未加密。

adaption_field_control

翻译为“调整字段控制”,表示TS分组首部后面是否跟随有调整字段和有效负载。01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00的话解码器不进行处理。空分组没有调整字段

continuity_counter

一个4bit的计数器,范围0-15,具有相同的PID的TS分组传输时每次加1,到15后清0。不过,有些情况下是不计数的。如下:(1)TS分组无有效负载(2)复制的TS分组和原分组这个值一样(3)后面讲到的一个标志discontinuity_indicator为1时adaptation_field()

调整字段的处理data_byte

有效负载的剩余部分,可能为PES分组,PSI,或一些自定义的数据。(2)

PAT数据结构如下:

program_association_section(){

table_id

//8

section_syntax_indicator

//1

'0'

//1

reserved

//2

section_length

//12

transport_stream_id

//16

reserved

//2

version_number

//5

current_next_indicator

//1

section_number

//8

last_section_number

//8

for(i=0;i<N;i++){

program_number

//16

reserved

//3

if(program_number=='0'){

network_PID

//13

}

else{

program_map_PID

//13

}

}

CRC_32

//

32

}table_id

固定为0x00

,标志是该表是PATsection_syntax_indicator

段语法标志位,固定为1section_length

表示这个字节后面有用的字节数,包括CRC32。假如后面的字节加上前面的字节数少于188,后面会用0XFF填充。假如这个数值比较大,则PAT会分成几部分来传输。transport_stream_id

该传输流的ID,区别于一个网络中其它多路复用的流。version_number范围0-31,表示PAT的版本号,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经变化了,程序必须重新搜索节目.current_next_indicator

表示发送的PAT是当前有效还是下一个PAT有效。section_number分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段last_section_number

最后一个分段的号码

program_number

节目号network_PID

网络信息表(NIT)的PID,网络信息表提供了该物理网络的一些信息,和电视台相关的。节目号为0时对应的PID为network_PIDprogram_map_PID

节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个CRC_32

CRC32校验码上面program_number,network_PID,program_map_PID

是循环出现的。program_number等于0时对应network_PID,program_number等于其它值时对应program_map_PID。(3)PMT

PMT数据结构如下:

TS_program_map_section(){

table_id

//8

section_syntax_indicator

//

1

'0'

//

1

reserved

//

2

section_length

//

12

program_number

//

16

reserved

//

2

version_number

//

5

current_next_indicator

//

1

section_number

//

8

last_section_number

//

8

reserved

//

3

PCR_PID

//

13

reserved

4

program_info_length

//

12

for(i=0;i<N;i++){

descriptor()

}

for(i=0;i<N1;i++){

stream_type

//

8

reserved

//

3

elementary_PID

//

13

reserved

//

4

ES_info_length

//

12

for(i=0;i<N2;i++){

descriptor()

}

}

CRC_32

//

32

}table_id

固定为0x02

,标志是该表是PMT。section_syntax_indicator

section_length

version_number

current_next_indicator

以上四个字段意思和PAT相同,可参考上面解释section_number

last_section_number

以上两个字段意思和PAT相同,不过值都固定为0x00,我觉得这样的原因可能是因为PMT不需要有先后顺序,因为先定义哪个节目都是无所谓。program_number

节目号,表示该PMT对应的节目PCR_PID

PCR(节目时钟参考)所在TS分组的PID,根据PID可以去搜索相应的TS分组,解出PCR信息。program_info_length

该节目的信息长度,在此字段之后可能会有一些字节描述该节目的信息stream_type

指示了PID为elementary_PID的PES分组中原始流的类型,比如视频流,音频流等,见后面的表elementary_PID

该节目中包括的视频流,音频流等对应的TS分组的PIDES_info_length

该节目相关原始流的描述符的信息长度。5.TS流的解析实例讲解TS流中的每个Packet为188个字节,一个section可能由一个或多个Packet组成,而一个Table(如PAT、PMT、NIT等)则由多个Section组成,同一个表中的Section通过Tableid来加以细分。在读取TS流文件时我们每次读取188个字节,进而形成Packet队列和Section队列。Packet队列可以根据包的PID的值的不同添加到相应的队列中。几种PSI之间的关系:首先PAT中指定了传输流中所存在的节目,及每个节目对应的PMT的PID号。

比如Program1对应的PMT

的PID=22,然后找到PID=22的TS分组,解出PMT,得到这个节目中包含的原始流的PID,再根据原始流的PID去找相应的TS分组,获取原始流的数据,然后就可以送入解码器解码了。

根据前一篇中各数据的定义及数据结构,对数据进行分别解析如下:现在以一个例子来说明具体的操作:

在开始之前先给出一片实际TS流例子:

0000f32ch:474000170000B00D0001C10000

0001E0;G@....?..?...?

0000f33ch:20A2C32941FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

⒚)A

0000f34ch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f35ch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f36ch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f37ch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f38ch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f39ch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f3ach:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f3bch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f3cch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF;

0000f3dch:FFFF

FF

FF

FF

FF

FF

FF

FF

FF

FF

FF47402017;G@.

0000f3ech:0002B01B0001C10000E021F0001BE021;..?..?.??.?

0000f3fch:F0042A027E1F03E022F0005D16BD48

;具体的分析就以这个例子来分析。//AdjustTSpacketheader

voidadjust_TS_packet_header(TS_packet_header*pheader)

{

unsignedcharbuf[4];

memcpy(buf,pheader,4);

pheader->transport_error_indicator

=buf[1]>>7;

pheader->payload_unit_start_indicator

=buf[1]>>6&0x01;

pheader->transport_priority=buf[1]>>5&0x01;

pheader->PID

=(buf[1]&0x1F)<<8|buf[2];

pheader->transport_scrambling_control

=buf[3]>>6;

pheader->adaption_field_control

=buf[3]>>4&0x03;

pheader->continuity_counter

=buf[3]&0x03;

}

这是一个调整TS流数据包头的函数,这里牵扯到位段调整的问题。现在看一下TS流数据包头的结构的定义://Transportpacketheader

typedef

struct

TS_packet_header

{

unsignedsync_byte

:8;

unsignedtransport_error_indicator

:1;

unsignedpayload_unit_start_indicator

:1;

unsignedtransport_priority

:1;

unsignedPID

:13;

unsignedtransport_scrambling_control

:2;

unsignedadaption_field_control

:2;

unsignedcontinuity_counter

:4;

}TS_packet_header;

下面我们来分析,在ISO/IEC13818-1里有说明,PAT(ProgramAssociationTable)的PID值为0x00,TS包的标识(即sync_byte)为0x47,并且为了确保这个TS包里的数据有效,所以我们一开始查找474000这三组16进制数,为什么这样?具体的奥秘在TS包的结构上,前面已经说了sync_byte固定为0x47。现在往下看transport_error_indicator、payload_unit_start_indicator、transport_priority和PID这四个元素,PID为0x00,这是PAT的标识。transport_error_indicator为0,transport_priority为0。把他们看成是两组8位16进制数就是:4000。现在看看我们的TS流片断例子,看来正好是474000开头的,一个TS流的头部占据了4个字节。剩下的负载部分的内容由PID来决定,例子看来就是一个PAT表。在这里有个地方需要注意一下,payload_unit_start_indicator为1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。现在看例子中的数据4740001700第五个字节是00,说明紧跟着00之后就是具体的负载内容。下面给出PAT表的结构体://PATtable//ProgrammAssociationTable

typedef

structTS_PAT

{

unsignedtable_id

:8;

unsignedsection_syntax_indicator

:1;

unsignedzero

:1;

unsignedreserved_1

:2;

unsignedsection_length

:12;

unsignedtransport_stream_id

:16;

unsignedreserved_2

:2;

unsignedversion_number

:5;

unsignedcurrent_next_indicator

:1;

unsignedsection_number

:8;

unsignedlast_section_number

:8;

unsignedprogram_number

:16;

unsignedreserved_3

:3;

unsignednetwork_PID

:13;

unsignedprogram_map_PID

:13;

unsignedCRC_32

:32;

}TS_PAT;

再给出PAT表字段调整函数://AdjustPATtable

voidadjust_PAT_table(TS_PAT*packet,char*buffer)

{

intn=0,i=0;

int

len=0;

packet->table_id

=buffer[0];

packet->section_syntax_indicator

=buffer[1]>>7;

packet->zero

=buffer[1]>>6&0x1;

packet->reserved_1

=buffer[1]>>4&0x3;

packet->section_length

=(buffer[1]&0x0F)<<8|buffer[2];

packet->transport_stream_id=buffer[3]<<8|buffer[4];

packet->reserved_2

=buffer[5]>>6;

packet->version_number

=buffer[5]>>1&

0x1F;

packet->current_next_indicator

=(buffer[5]<<7)>>7;

packet->section_number

=buffer[6];

packet->last_section_number

=buffer[7];//GetCRC_32

len=3+packet->section_length;

packet->CRC_32=(buffer[len-4]&0x000000FF)<<24

|(buffer[len-3]&0x000000FF)<<16

|(buffer[len-2]&0x000000FF)<<8

|(buffer[len-1]&0x000000FF);//Parsenetwork_PIDorprogram_map_PID

for(n=0;n<packet->section_length-4;n++)

{

packet->program_number

=buffer[8]<<8|buffer[9];

packet->reserved_3

=buffer[10]>>5;

if(packet->program_number==0x0)

packet->network_PID=(buffer[10]<<3)<<5|buffer[11];

else

{

packet->program_map_PID=(buffer[10]<<3)<<5|buffer[11];

}

n+=5;

}

}

通过上面的分析,例子中的数据00B00D0001C10000

0001E020A2C32941就是具体的PAT表的内容,然后根据PAT结构体来具体分析PAT表。但是我们需要注意的是在PAT表里有program_number、network_PID的元素不只有一个,这两个元素是通过循环来确定的。循环的次数通过section_length元素的确定。在这个例子中program_map_PID为20,所以下面来PMT分析时,就是查找474020的开头的TS包。

下面来分析PMT表,先给出PMT(ProgramMapTable)的结构体:

//PMTtable

//ProgramMapTable

typedef

structTS_PMT

{

unsignedtable_id

:8;

unsignedsection_syntax_indicator

:1;

unsignedzero

:1;

unsignedreserved_1

:2;

unsignedsection_length

:12;

unsignedprogram_number

:16;

unsignedreserved_2

:2;

unsignedversion_number

:5;

unsignedcurrent_next_indicator

:1;

unsignedsection_number

:8;

unsignedlast_section_number

:8;

unsignedreserved_3

:3;

unsignedPCR_PID

:13;

unsignedreserved_4

:4;

unsignedprogram_info_length

:12;

unsignedstream_type

:8;

unsignedreserved_5

:3;

unsignedelementary_PID

:13;

unsignedreserved_6

:4;

unsignedES_info_length

:12;

unsignedCRC_32

:32;

}TS_PMT;

在给出调整字段函数:

//AdjustPMTtable

voidadjust_PMT_table(TS_PMT*packet,char*buffer)

{

intpos=12,len=0;

inti=0;

packet->table_id

=buffer[0];

packet->section_syntax_indicator

=buffer[1]>>7;

packet->zero

=buffer[1]>>6;

packet->reserved_1

=buffer[1]>>4;

packet->section_length

=(buffer[1]&0x0F)<<8|buffer[2];

packet->program_number=buffer[3]<<8|buffer[4];packet->reserved_2

=buffer

温馨提示

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

评论

0/150

提交评论