(转)基于RTP的H264视频数据打包解包类

最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包、解包的文档和代码。功夫不负有心人,找到不少有价值的文档和代码。参考这些资料,写了H264 RTP打包类、解包类,实现了单个NAL单元包和FU_A分片单元包。对于丢包处理,采用简单的策略:丢弃随后的所有数据包,直到收到关键帧。测试效果还不错,代码贴上来,若能为同道中人借鉴一二,足矣。两个类的使用说明如下(省略了错误处理过程):

DWORD H264SSRC ;
 CH264_RTP_PACK pack ( H264SSRC ) ;
 BYTE *pVideoData ;
 DWORD Size, ts ;
 bool IsEndOfFrame ;
 WORD wLen ;
 pack.Set ( pVideoData, Size, ts, IsEndOfFrame ) ;
 BYTE *pPacket ;
 while ( pPacket = pack.Get ( &wLen ) )
 {
  // rtp packet process
  // ...
 }

HRESULT hr ;
 CH264_RTP_UNPACK unpack ( hr ) ;
 BYTE *pRtpData ;
 WORD inSize;
 int outSize ;
 BYTE *pFrame = unpack.Parse_RTP_Packet ( pRtpData, inSize, &outSize ) ;
 if ( pFrame != NULL )
 {
  // frame process
  // ...
 }

//////////////////////////////////////////////////////////////////////////////////////////
// class CH264_RTP_PACK start

class CH264_RTP_PACK
{
    #define RTP_VERSION 2

    typedef struct NAL_msg_s
    {
        bool eoFrame ;
        unsigned char type;        // NAL type
        unsigned char *start;    // pointer to first location in the send buffer
        unsigned char *end;    // pointer to last location in send buffer
        unsigned long size ;
    } NAL_MSG_t;

    typedef struct
    {
        //LITTLE_ENDIAN
        unsigned short   cc:4;        /* CSRC count                 */
        unsigned short   x:1;        /* header extension flag      */
        unsigned short   p:1;        /* padding flag               */
        unsigned short   v:2;        /* packet type                */
        unsigned short   pt:7;        /* payload type               */
        unsigned short   m:1;        /* marker bit                 */

        unsigned short    seq;        /* sequence number            */
        unsigned long     ts;        /* timestamp                  */
        unsigned long     ssrc;        /* synchronization source     */
    } rtp_hdr_t;

    typedef struct tagRTP_INFO
    {
        NAL_MSG_t    nal;        // NAL information
        rtp_hdr_t    rtp_hdr;    // RTP header is assembled here
        int hdr_len;            // length of RTP header

        unsigned char *pRTP;    // pointer to where RTP packet has beem assembled
        unsigned char *start;    // pointer to start of payload
        unsigned char *end;        // pointer to end of payload

        unsigned int s_bit;        // bit in the FU header
        unsigned int e_bit;        // bit in the FU header
        bool FU_flag;        // fragmented NAL Unit flag
    } RTP_INFO;

public:
    CH264_RTP_PACK(unsigned long H264SSRC, unsigned char H264PAYLOADTYPE=96, unsigned short MAXRTPPACKSIZE=1472 )
    {
        m_MAXRTPPACKSIZE = MAXRTPPACKSIZE ;
        if ( m_MAXRTPPACKSIZE > 10000 )
        {
            m_MAXRTPPACKSIZE = 10000 ;
        }
        if ( m_MAXRTPPACKSIZE < 50 )
        {
            m_MAXRTPPACKSIZE = 50 ;
        }

        memset ( &m_RTP_Info, 0, sizeof(m_RTP_Info) ) ;

        m_RTP_Info.rtp_hdr.pt = H264PAYLOADTYPE ;
        m_RTP_Info.rtp_hdr.ssrc = H264SSRC ;
        m_RTP_Info.rtp_hdr.v = RTP_VERSION ;

        m_RTP_Info.rtp_hdr.seq = 0 ;
    }

    ~CH264_RTP_PACK(void)
    {
    }

    //传入Set的数据必须是一个完整的NAL,起始码为0x00000001。
    //起始码之前至少预留10个字节,以避免内存COPY操作。
    //打包完成后,原缓冲区内的数据被破坏。
    bool Set ( unsigned char *NAL_Buf, unsigned long NAL_Size, unsigned long Time_Stamp, bool End_Of_Frame )
    {
        unsigned long startcode = StartCode(NAL_Buf) ;

        if ( startcode != 0x01000000 )
        {
            return false ;
        }

        int type = NAL_Buf[4] & 0x1f ;
        if ( type < 1 || type > 12 )
        {
            return false ;
        }

        m_RTP_Info.nal.start = NAL_Buf ;
        m_RTP_Info.nal.size = NAL_Size ;
        m_RTP_Info.nal.eoFrame = End_Of_Frame ;
        m_RTP_Info.nal.type = m_RTP_Info.nal.start[4] ;
        m_RTP_Info.nal.end = m_RTP_Info.nal.start + m_RTP_Info.nal.size ;

        m_RTP_Info.rtp_hdr.ts = Time_Stamp ;

        m_RTP_Info.nal.start += 4 ;    // skip the syncword

        if ( (m_RTP_Info.nal.size + 7) > m_MAXRTPPACKSIZE )
        {
            m_RTP_Info.FU_flag = true ;
            m_RTP_Info.s_bit = 1 ;
            m_RTP_Info.e_bit = 0 ;

            m_RTP_Info.nal.start += 1 ;    // skip NAL header
        }
        else
        {
            m_RTP_Info.FU_flag = false ;
            m_RTP_Info.s_bit = m_RTP_Info.e_bit = 0 ;
        }

        m_RTP_Info.start = m_RTP_Info.end = m_RTP_Info.nal.start ;
        m_bBeginNAL = true ;

        return true ;
    }

    //循环调用Get获取RTP包,直到返回值为NULL
    unsigned char* Get ( unsigned short *pPacketSize )
    {
        if ( m_RTP_Info.end == m_RTP_Info.nal.end )
        {
            *pPacketSize = 0 ;
            return NULL ;
        }

        if ( m_bBeginNAL )
        {
            m_bBeginNAL = false ;
        }
        else
        {
            m_RTP_Info.start = m_RTP_Info.end;    // continue with the next RTP-FU packet
        }

        int bytesLeft = m_RTP_Info.nal.end - m_RTP_Info.start ;
        int maxSize = m_MAXRTPPACKSIZE - 12 ;    // sizeof(basic rtp header) == 12 bytes
        if ( m_RTP_Info.FU_flag )
            maxSize -= 2 ;

        if ( bytesLeft > maxSize )
        {
            m_RTP_Info.end = m_RTP_Info.start + maxSize ;    // limit RTP packetsize to 1472 bytes
        }
        else
        {
            m_RTP_Info.end = m_RTP_Info.start + bytesLeft ;
        }

        if ( m_RTP_Info.FU_flag )
        {    // multiple packet NAL slice
            if ( m_RTP_Info.end == m_RTP_Info.nal.end )
            {
                m_RTP_Info.e_bit = 1 ;
            }
        }

        m_RTP_Info.rtp_hdr.m =    m_RTP_Info.nal.eoFrame ? 1 : 0 ; // should be set at EofFrame
        if ( m_RTP_Info.FU_flag && !m_RTP_Info.e_bit )
        {
            m_RTP_Info.rtp_hdr.m = 0 ;
        }

        m_RTP_Info.rtp_hdr.seq++ ;

        unsigned char *cp = m_RTP_Info.start ;
        cp -= ( m_RTP_Info.FU_flag ? 14 : 12 ) ;
        m_RTP_Info.pRTP = cp ;

        unsigned char *cp2 = (unsigned char *)&m_RTP_Info.rtp_hdr ;
        cp[0] = cp2[0] ;
        cp[1] = cp2[1] ;

        cp[2] = ( m_RTP_Info.rtp_hdr.seq >> 8 ) & 0xff ;
        cp[3] = m_RTP_Info.rtp_hdr.seq & 0xff ;

        cp[4] = ( m_RTP_Info.rtp_hdr.ts >> 24 ) & 0xff ;
        cp[5] = ( m_RTP_Info.rtp_hdr.ts >> 16 ) & 0xff ;
        cp[6] = ( m_RTP_Info.rtp_hdr.ts >>  8 ) & 0xff ;
        cp[7] = m_RTP_Info.rtp_hdr.ts & 0xff ;

        cp[8] =  ( m_RTP_Info.rtp_hdr.ssrc >> 24 ) & 0xff ;
        cp[9] =  ( m_RTP_Info.rtp_hdr.ssrc >> 16 ) & 0xff ;
        cp[10] = ( m_RTP_Info.rtp_hdr.ssrc >>  8 ) & 0xff ;
        cp[11] = m_RTP_Info.rtp_hdr.ssrc & 0xff ;
        m_RTP_Info.hdr_len = 12 ;
        /*!
        * /n The FU indicator octet has the following format:
        * /n
        * /n      +---------------+
        * /n MSB  |0|1|2|3|4|5|6|7|  LSB
        * /n      +-+-+-+-+-+-+-+-+
        * /n      |F|NRI|  Type   |
        * /n      +---------------+
        * /n
        * /n The FU header has the following format:
        * /n
        * /n      +---------------+
        * /n      |0|1|2|3|4|5|6|7|
        * /n      +-+-+-+-+-+-+-+-+
        * /n      |S|E|R|  Type   |
        * /n      +---------------+
        */
        if ( m_RTP_Info.FU_flag )
        {
            // FU indicator  F|NRI|Type
            cp[12] = ( m_RTP_Info.nal.type & 0xe0 ) | 28 ;    //Type is 28 for FU_A
            //FU header        S|E|R|Type
            cp[13] = ( m_RTP_Info.s_bit << 7 ) | ( m_RTP_Info.e_bit << 6 ) | ( m_RTP_Info.nal.type & 0x1f ) ; //R = 0, must be ignored by receiver

            m_RTP_Info.s_bit = m_RTP_Info.e_bit= 0 ;
            m_RTP_Info.hdr_len = 14 ;
        }
        m_RTP_Info.start = &cp[m_RTP_Info.hdr_len] ;    // new start of payload

        *pPacketSize = m_RTP_Info.hdr_len + ( m_RTP_Info.end - m_RTP_Info.start ) ;
        return m_RTP_Info.pRTP ;
    }

private:
    unsigned int StartCode( unsigned char *cp )
    {
        unsigned int d32 ;
        d32 = cp[3] ;
        d32 <<= 8 ;
        d32 |= cp[2] ;
        d32 <<= 8 ;
        d32 |= cp[1] ;
        d32 <<= 8 ;
        d32 |= cp[0] ;
        return d32 ;
    }

private:
    RTP_INFO m_RTP_Info ;
    bool m_bBeginNAL ;
    unsigned short m_MAXRTPPACKSIZE ;
};

// class CH264_RTP_PACK end
//////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
// class CH264_RTP_UNPACK start

class CH264_RTP_UNPACK
{

#define RTP_VERSION 2
#define BUF_SIZE (1024 * 500)

    typedef struct
    {
        //LITTLE_ENDIAN
        unsigned short   cc:4;        /* CSRC count                 */
        unsigned short   x:1;        /* header extension flag      */
        unsigned short   p:1;        /* padding flag               */
        unsigned short   v:2;        /* packet type                */
        unsigned short   pt:7;        /* payload type               */
        unsigned short   m:1;        /* marker bit                 */

        unsigned short    seq;        /* sequence number            */
        unsigned long     ts;        /* timestamp                  */
        unsigned long     ssrc;        /* synchronization source     */
    } rtp_hdr_t;
public:

    CH264_RTP_UNPACK ( HRESULT &hr, unsigned char H264PAYLOADTYPE = 96 )
        : m_bSPSFound(false)
        , m_bWaitKeyFrame(true)
        , m_bPrevFrameEnd(false)
        , m_bAssemblingFrame(false)
        , m_wSeq(1234)
        , m_ssrc(0)
    {
        m_pBuf = new BYTE[BUF_SIZE] ;
        if ( m_pBuf == NULL )
        {
            hr = E_OUTOFMEMORY ;
            return ;
        }

        m_H264PAYLOADTYPE = H264PAYLOADTYPE ;
        m_pEnd = m_pBuf + BUF_SIZE ;
        m_pStart = m_pBuf ;
        m_dwSize = 0 ;
        hr = S_OK ;
    }

    ~CH264_RTP_UNPACK(void)
    {
        delete [] m_pBuf ;
    }

    //pBuf为H264 RTP视频数据包,nSize为RTP视频数据包字节长度,outSize为输出视频数据帧字节长度。
    //返回值为指向视频数据帧的指针。输入数据可能被破坏。
    BYTE* Parse_RTP_Packet ( BYTE *pBuf, unsigned short nSize, int *outSize )
    {
        if ( nSize <= 12 )
        {
            return NULL ;
        }

        BYTE *cp = (BYTE*)&m_RTP_Header ;
        cp[0] = pBuf[0] ;
        cp[1] = pBuf[1] ;

        m_RTP_Header.seq = pBuf[2] ;
        m_RTP_Header.seq <<= 8 ;
        m_RTP_Header.seq |= pBuf[3] ;

        m_RTP_Header.ts = pBuf[4] ;
        m_RTP_Header.ts <<= 8 ;
        m_RTP_Header.ts |= pBuf[5] ;
        m_RTP_Header.ts <<= 8 ;
        m_RTP_Header.ts |= pBuf[6] ;
        m_RTP_Header.ts <<= 8 ;
        m_RTP_Header.ts |= pBuf[7] ;

        m_RTP_Header.ssrc = pBuf[8] ;
        m_RTP_Header.ssrc <<= 8 ;
        m_RTP_Header.ssrc |= pBuf[9] ;
        m_RTP_Header.ssrc <<= 8 ;
        m_RTP_Header.ssrc |= pBuf[10] ;
        m_RTP_Header.ssrc <<= 8 ;
        m_RTP_Header.ssrc |= pBuf[11] ;

        BYTE *pPayload = pBuf + 12 ;
        DWORD PayloadSize = nSize - 12 ;

        // Check the RTP version number (it should be 2):
        if ( m_RTP_Header.v != RTP_VERSION )
        {
            return NULL ;
        }

        /*
        // Skip over any CSRC identifiers in the header:
        if ( m_RTP_Header.cc )
        {
            long cc = m_RTP_Header.cc * 4 ;
            if ( Size < cc )
            {
                return NULL ;
            }

            Size -= cc ;
            p += cc ;
        }

        // Check for (& ignore) any RTP header extension
        if ( m_RTP_Header.x )
        {
            if ( Size < 4 )
            {
                return NULL ;
            }

            Size -= 4 ;
            p += 2 ;
            long l = p[0] ;
            l <<= 8 ;
            l |= p[1] ;
            p += 2 ;
            l *= 4 ;
            if ( Size < l ) ;
            {
                return NULL ;
            }
            Size -= l ;
            p += l ;
        }

        // Discard any padding bytes:
        if ( m_RTP_Header.p )
        {
            if ( Size == 0 )
            {
                return NULL ;
            }
            long Padding = p[Size-1] ;
            if ( Size < Padding )
            {
                return NULL ;
            }
            Size -= Padding ;
        }*/

        // Check the Payload Type.
        if ( m_RTP_Header.pt != m_H264PAYLOADTYPE )
        {
            return NULL ;
        }

        int PayloadType = pPayload[0] & 0x1f ;
        int NALType = PayloadType ;
        if ( NALType == 28 ) // FU_A
        {
            if ( PayloadSize < 2 )
            {
                return NULL ;
            }

            NALType = pPayload[1] & 0x1f ;
        }

        if ( m_ssrc != m_RTP_Header.ssrc )
        {
            m_ssrc = m_RTP_Header.ssrc ;
            SetLostPacket () ;
        }

        if ( NALType == 0x07 ) // SPS
        {
            m_bSPSFound = true ;
        }

        if ( !m_bSPSFound )
        {
            return NULL ;
        }

        if ( NALType == 0x07 || NALType == 0x08 ) // SPS PPS
        {
            m_wSeq = m_RTP_Header.seq ;
            m_bPrevFrameEnd = true ;

            pPayload -= 4 ;
            *((DWORD*)(pPayload)) = 0x01000000 ;
            *outSize = PayloadSize + 4 ;
            return pPayload ;
        }

        if ( m_bWaitKeyFrame )
        {
            if ( m_RTP_Header.m ) // frame end
            {
                m_bPrevFrameEnd = true ;
                if ( !m_bAssemblingFrame )
                {
                    m_wSeq = m_RTP_Header.seq ;
                    return NULL ;
                }
            }

            if ( !m_bPrevFrameEnd )
            {
                m_wSeq = m_RTP_Header.seq ;
                return NULL ;
            }
            else
            {
                if ( NALType != 0x05 ) // KEY FRAME
                {
                    m_wSeq = m_RTP_Header.seq ;
                    m_bPrevFrameEnd = false ;
                    return NULL ;
                }
            }
        }

///////////////////////////////////////////////////////////////

        if ( m_RTP_Header.seq != (WORD)( m_wSeq + 1 ) ) // lost packet
        {
            m_wSeq = m_RTP_Header.seq ;
            SetLostPacket () ;
            return NULL ;
        }
        else
        {
            // 码流正常

            m_wSeq = m_RTP_Header.seq ;
            m_bAssemblingFrame = true ;

            if ( PayloadType != 28 ) // whole NAL
            {
                *((DWORD*)(m_pStart)) = 0x01000000 ;
                m_pStart += 4 ;
                m_dwSize += 4 ;
            }
            else // FU_A
            {
                if ( pPayload[1] & 0x80 ) // FU_A start
                {
                    *((DWORD*)(m_pStart)) = 0x01000000 ;
                    m_pStart += 4 ;
                    m_dwSize += 4 ;

                    pPayload[1] = ( pPayload[0] & 0xE0 ) | NALType ;

                    pPayload += 1 ;
                    PayloadSize -= 1 ;
                }
                else
                {
                    pPayload += 2 ;
                    PayloadSize -= 2 ;
                }
            }

            if ( m_pStart + PayloadSize < m_pEnd )
            {
                CopyMemory ( m_pStart, pPayload, PayloadSize ) ;
                m_dwSize += PayloadSize ;
                m_pStart += PayloadSize ;
            }
            else // memory overflow
            {
                SetLostPacket () ;
                return NULL ;
            }

            if ( m_RTP_Header.m ) // frame end
            {
                *outSize = m_dwSize ;

                m_pStart = m_pBuf ;
                m_dwSize = 0 ;

                if ( NALType == 0x05 ) // KEY FRAME
                {
                    m_bWaitKeyFrame = false ;
                }
                return m_pBuf ;
            }
            else
            {
                return NULL ;
            }
        }
    }

    void SetLostPacket()
    {
        m_bSPSFound = false ;
        m_bWaitKeyFrame = true ;
        m_bPrevFrameEnd = false ;
        m_bAssemblingFrame = false ;
        m_pStart = m_pBuf ;
        m_dwSize = 0 ;
    }

private:
    rtp_hdr_t m_RTP_Header ;

    BYTE *m_pBuf ;

    bool m_bSPSFound ;
    bool m_bWaitKeyFrame ;
    bool m_bAssemblingFrame ;
    bool m_bPrevFrameEnd ;
    BYTE *m_pStart ;
    BYTE *m_pEnd ;
    DWORD m_dwSize ;

    WORD m_wSeq ;

    BYTE m_H264PAYLOADTYPE ;
    DWORD m_ssrc ;
};

// class CH264_RTP_UNPACK end
//////////////////////////////////////////////////////////////////////////////////////////

参考:

1,基于RTP的H264视频数据打包解包类

http://blog.csdn.net/dengzikun/article/details/5807694

时间: 2024-10-13 22:56:29

(转)基于RTP的H264视频数据打包解包类的相关文章

Linux-6.5下 MariaDB-10基于LVM快照的备份数据 详解

理解部分: LVM是逻辑盘卷管理(Logical Volume Manager)的简称,它是Linux环境下对磁盘分区进行管理的一种机制,LVM是建立在硬盘和分区之上的一个逻辑层,来提高磁盘分区管理的灵活性.LVM可以对分区在线扩容,快照,镜像和条带化,功能非常强大.这篇文章的主题就是其中一个功能--快照. 快照(Snapshot)就是关于指定数据集合的一个完全可用拷贝,该拷贝包括相应数据在某个时间点(拷贝开始的时间点)的映像.快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品: 其实

Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口

Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器内的元素的遍历 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构.迭代器通常被称为"轻量级"对象,因为创建它的代价小. Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个I

java基础类型包装类与自动打包解包

一基础类型包装类 基础数据类型存放在内存的栈区域,可以通过包装类将基础数据类型转换为引用数据类型,即存储在堆与栈中. 基础数据类型一共有8种,相对应的包装类也有8种.分别是Byte,Short,Integer,Long,Float,Double,Character,Boolean.类 包装类位于java.Lang包中. 1 public class Test1 { 2 3 public static void main(String[] args) { 4 // TODO Auto-genera

xpack文件打包解包代码库

Github ###概述 xpack是一个文件资源打包工具及类库,可以对多文件进行打包解包. 其使用文件名的hash作为索引,建立hash索引表以加速文件查找. ###特性 支持hashid自动解冲突,即使添加的多个文件名hashid发生冲突也可以正常存取,不用担心文件名hash一样导致的添加失败问题 支持存储文件的原文件名,可以解包复原文件目录结构 支持文件分块存储,可以重复利用被删除文件的剩下的空闲块,提高资源包的空间利用率,提高资源删除的性能 支持寄生资源包,可以把资源包追加到其他文件中,

压缩/打包/解包 命令详解

压缩&解压 gzip gzip 压缩级别1-9 1 压缩级别最低 9压缩级别最高 [默认级别6] 压缩文件类型.gz 压缩:gzip 压缩文件名 [修改压缩级别方法:gzip -1 压缩文件名称] 解压:gzip -d 解压文件名称/gunzip 解压文件名 查看压缩文件:zcat压缩文件名 gzip 只支持文件,不支持目录 bzip2 压缩文件类型.bz2 [也是无法压缩目录.只能压缩文件] 压缩:bzip2 文件名称 解压:bzip2 -d 文件名称/bunzip2 文件名 查看压缩文件:b

打包/解包/压缩

命令: tar 作用: 可以把文件一系列打包到一个大文件中,也可以把打包过的大文件恢复成一系列文件 选项: -z:调用gzip,压缩和解压缩文件 -j:调用bzip2,压缩和解压文件 -c:生成档案文件,创建打包文件 -x:解开档案文件 -v:列出归档解档的详细过程,显示进度 -f:指定档案文件名称,f后面一定是.tar文件,所以必须放选项最后 -C:解压缩到指定目录(要解压缩的目录必须存在) 注意: f选项必须放最后,其他选项顺序随意 使用: # 打包文件(被打包文件可以空格间隔输入多个) t

打包解包

打tar包 tar cvf .tar包名 包源文件 tar cvf task.tar t1 t2 在tar包基础上,打gz包 gzip .tar包:命令运行后,task.tar->task.tar.gz gzip task.tar 将gz包解压为tar包,命令运行后,task.tar.gz->task.tar gzip -d task.tar.gz 查看tar包 tar tvf pack1.tar 解tar包 tar xvf pack1.tar 打gz包,一步到位 tar cvfz task.

apktool 打包解包apk的总结

1) 不需要另外下载 baksmali-2.1.2.jar, apktool.jar 好像都包含了. apktool d zhanqi.xxx.apk -o zhanqi 2) smalidea-0.03.zip 的 Android Studio插件装上之后,就可以调试 1) 生成的 smali 文件了 https://github.com/JesusFreke/smali/wiki/smalidea Manually disassemble an application using baksm

基于Dedup的数据打包技术

0.引言    Tar, winrar, winzip是最为常见的数据打包工具软件,它们把文件集体封装成一个单独的数据包,从而方便数据的分布.传输.归档以及持久保存等目的.这类工具通常都支持数据压缩技术,从而有效减少数据的存储空间,常用压缩算法有Huffman编码.Z77/z78.LZW等.压缩算法的原理是通过对数据的重新编码,高频率数据片段采用较短的编码,低频率数据片段采用较长的编码,从而获得全局的上数据量较小的文件表示. 1.Dedup原理   Deduplication,即重复数据删除,它