/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <string.h>
#include "h264_information.h"

//#define DEBUG_SEI_MESSAGE 1

#ifdef DEBUG_SEI_MESSAGE
    #include "bitstream_parser.h"
    #include <stdio.h>
    #include <math.h>

    WebRtc_UWord32 BitRateBPS(WebRtc_UWord16 x )
    {
        return (x & 0x3fff) * WebRtc_UWord32(pow(10.0f,(2 + (x >> 14))));
    }

#endif

namespace webrtc {
H264Information::H264Information(const bool SVC)
    : _SVC(SVC)

{
}

H264Information::~H264Information()
{

}

void
H264Information::Reset()
{
    _parsedLength = 0;
    _remLength = 0;
    _length = 0;
    _info.numNALUs = 0;
    _info.numLayers = 0;

    memset(_info.startCodeSize, 0, sizeof(_info.startCodeSize));
    memset(_info.payloadSize, 0, sizeof(_info.payloadSize));
    memset(_info.NRI, 0, sizeof(_info.NRI));
    memset(_info.type, 0, sizeof(_info.type));
    memset(_info.accLayerSize, 0, sizeof(_info.accLayerSize));

    for (WebRtc_Word32 i = 0; i < KMaxNumberOfNALUs; i++)
    {
        _info.SVCheader[i].idr =            0;
        _info.SVCheader[i].priorityID =     0;
        _info.SVCheader[i].interLayerPred = 0;
        _info.SVCheader[i].dependencyID =   0;
        _info.SVCheader[i].qualityID =      0;
        _info.SVCheader[i].temporalID =     0;
        _info.SVCheader[i].useRefBasePic =  0;
        _info.SVCheader[i].discardable =    0;
        _info.SVCheader[i].output =         0;

        _info.PACSI[i].X = 0;
        _info.PACSI[i].Y = 0;
//      _info.PACSI[i].T = 0;
        _info.PACSI[i].A = 0;
        _info.PACSI[i].P = 0;
        _info.PACSI[i].C = 0;
        _info.PACSI[i].S = 0;
        _info.PACSI[i].E = 0;
        _info.PACSI[i].TL0picIDx =   0;
        _info.PACSI[i].IDRpicID =    0;
        _info.PACSI[i].DONC =        0;
        _info.PACSI[i].numSEINALUs = 0;
        _info.PACSI[i].NALlength =   5;
    }
}

/*******************************************************************************
 * WebRtc_Word32 GetInfo(const WebRtc_UWord8* ptrEncodedBuffer,
 *             const WebRtc_UWord32 length,
 *             const H264Info*& ptrInfo);
 *
 * Gets information from an encoded stream.
 *
 * Input:
 *          - ptrEncodedBuffer  : Pointer to encoded stream.
 *          - length            : Length in bytes of encoded stream.
 *
 * Output:
 *          - ptrInfo           : Pointer to struct with H.264 info.
 *
 * Return value:
 *          - 0                 : ok
 *          - (-1)              : Error
 */
WebRtc_Word32
H264Information::GetInfo(const WebRtc_UWord8* ptrEncodedBuffer,
                             const WebRtc_UWord32 length,
                             const H264Info*& ptrInfo)
{
    if (!ptrEncodedBuffer || length < 4)
    {
        return -1;
    }

    if (!HasInfo(length))
    {
        if (-1 == FindInfo(ptrEncodedBuffer, length))
        {
            Reset();
            return -1;
        }
    }
    ptrInfo = &_info;
    return 0;
}

RtpVideoCodecTypes
H264Information::Type()
{
    if(_SVC)
    {
        return RTP_H264_SVCVideo;
    }
    return RTP_H264Video;
}


/*******************************************************************************
 * bool HasInfo(const WebRtc_UWord32 length);
 *
 * Checks if information has already been stored for this encoded stream.
 *
 * Input:
 *          - length            : Length in bytes of encoded stream.
 *
 * Return value:
 *          - true (false)      : Information has (not) been stored.
 */

bool
H264Information::HasInfo(const WebRtc_UWord32 length)
{
    if (!_info.numNALUs)
    {
        return false;
    }

    // has info, make sure current length matches info length
    if (length != _length)
    {
        Reset();
        return false;
    }

    return true;
}

/*******************************************************************************
 * WebRtc_Word32 FindInfo(const WebRtc_UWord8* ptrEncodedBuffer,
 *              const WebRtc_UWord32 length);
 *
 * Parses the encoded stream.
 *
 * Input:
 *          - ptrEncodedBuffer  : Pointer to encoded stream.
 *          - length            : Length in bytes of encoded stream.
 *
 * Return value:
 *          - 0                 : ok
 *          - (-1)              : Error
 */
WebRtc_Word32
H264Information::FindInfo(const WebRtc_UWord8* ptrEncodedBuffer, const WebRtc_UWord32 length)
{
    _ptrData = ptrEncodedBuffer;
    _length = length;
    _parsedLength = 0;
    _remLength = length;

    do
    {
        // Get start code length
        if (FindNALUStartCodeSize() == -1)
        {
            Reset();
            return -1;
        }

        // Get NAL unit payload size
        WebRtc_Word32 foundLast = FindNALU();
        if (foundLast == -1)
        {
            Reset();
            return -1;
        }

        // Validate parsed length
        if (_parsedLength > _length)
        {
            Reset();
            return -1;
        }

        // Get NRI
        GetNRI();

        // Get type
        if (FindNALUType() == -1)
        {
            Reset();
            return -1;
        }

        // Set layer start end bit
        SetLayerSEBit(foundLast);


        // Last NAL unit found?
        if (foundLast == 1)
        {
            if (_parsedLength != _length)
            {
                Reset();
                return -1;
            }
            _info.numNALUs++;
            return SetLayerLengths();
        }

        // Next NAL unit
        _ptrData   += (_info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]);
        _remLength -= (_info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]);
        _info.numNALUs++;

        // Validate memory allocation
        if (_info.numNALUs >= KMaxNumberOfNALUs)
        {
            Reset();
            return -1;
        }
    }
    while(true);

    return 0;
}

/*******************************************************************************
 * WebRtc_Word32 FindNALUStartCodeSize();
 *
 * Finds the start code length of the current NAL unit.
 *
 * Output:
 *          - _info.startCodeSize[currentNALU]  : Start code length in bytes of NAL unit.
 *
 * Return value:
 *          - 0                 : ok
 *          - (-1)              : Error
 */
WebRtc_Word32
H264Information::FindNALUStartCodeSize()
{
    // NAL unit start code. Ex. {0,0,1} or {0,0,0,1}
    for (WebRtc_UWord32 i = 2; i < _remLength; i++)
    {
        if (_ptrData[i] == 1 && _ptrData[i - 1] == 0 && _ptrData[i - 2] == 0)
        {
            _info.startCodeSize[_info.numNALUs] = WebRtc_UWord8(i + 1);
            return 0;
        }
    }
    return -1;
}

/*******************************************************************************
 * WebRtc_Word32 FindNALU();
 *
 * Finds the length of the current NAL unit.
 *
 * Output:
 *          - _info.payloadSize[currentNALU]  : Payload length in bytes of NAL unit
 *                                              (start code length not included).
 *          - _parsedLength                   : Current parsed length in bytes.
 *
 * Return value:
 *          - 1                 : ok. Last NAL unit found.
 *          - 0                 : ok
 *          - (-1)              : Error
 */
WebRtc_Word32
H264Information::FindNALU()
{
    for (WebRtc_UWord32 i = _info.startCodeSize[_info.numNALUs]; i < _remLength - 2; i += 2)
    {
        if (_ptrData[i] == 0)
        {
            WebRtc_Word32 size = 0;
            if ((_ptrData[i + 1] == 1 && _ptrData[i - 1] == 0) ||
                (_ptrData[i + 2] == 1 && _ptrData[i + 1] == 0))
            {
                // Found a header
                // Reduce size by preceding zeroes
                while (_ptrData[i - 1] == 0)
                {
                    i--;
                }
                size = i;
            }
            if (size > 0)
            {
                _info.payloadSize[_info.numNALUs] = size - _info.startCodeSize[_info.numNALUs];
                _parsedLength += _info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs];
                return 0;
            }
        }
    }
    // Last NAL unit
    _info.payloadSize[_info.numNALUs] = _remLength - _info.startCodeSize[_info.numNALUs];
    if (_info.payloadSize[_info.numNALUs] > 0)
    {
        _parsedLength += _info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs];
        return 1;
    }
    return -1;
}

/*******************************************************************************
 * void GetNRI();
 *
 * Finds the NRI of the current NAL unit.
 *
 * Output:
 *          - _info.NRI[currentNALU]   : NRI of NAL unit.
 *
 * Return value:
 *          - 0                        : ok
 *          - (-1)                     : Error
 */
void
H264Information::GetNRI()
{
    //  NAL unit header (1 byte)
    //  ---------------------------------
    // |   start code    |F|NRI|  Type   |
    //  ---------------------------------

    // NRI (2 bits) - nal_ref_idc. '00' - the NAL unit is not used to reconstruct reference pictures.
    //                             >00  - the NAL unit is required to reconstruct reference pictures
    //                                    in the same layer, or contains a parameter set.


    const WebRtc_UWord8 type = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x1f;

    // NALU type of 5, 7 and 8 shoud have NRI to b011
    if( type == 5 ||
        type == 7 ||
        type == 8)
    {
        _info.NRI[_info.numNALUs] = 0x60;
    }else
    {
        _info.NRI[_info.numNALUs] = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x60;
    }
}


/*******************************************************************************
 * WebRtc_Word32 FindNALUType();
 *
 * Finds the type of the current NAL unit.
 *
 * Output:
 *          - _info.type[currentNALU]  : Type of NAL unit
 *
 * Return value:
 *          - 0                        : ok
 *          - (-1)                     : Error
 */
WebRtc_Word32
H264Information::FindNALUType()
{
    //  NAL unit header (1 byte)
    //  ---------------------------------
    // |   start code    |F|NRI|  Type   |
    //  ---------------------------------

    _info.type[_info.numNALUs] = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x1f;

    if (_info.type[_info.numNALUs] == 0)
    {
        return -1;
    }

    // SVC NAL units, extended header
    if (ParseSVCNALUHeader() == -1)
    {
        return -1;
    }

    return 0;
}

/*******************************************************************************
 * WebRtc_Word32 ParseSVCNALUHeader();
 *
 * Finds the extended header of the current NAL unit. Included for NAL unit types 14 and 20.
 *
 * Output:
 *          - _info.SVCheader[currentNALU]  : SVC header of NAL unit.
 *
 * Return value:
 *          - 0                             : ok
 *          - (-1)                          : Error
 */
WebRtc_Word32
H264Information::ParseSVCNALUHeader()
{
    if (_info.type[_info.numNALUs] == 5)
    {
        _info.SVCheader[_info.numNALUs].idr = 1;
    }
    if (_info.type[_info.numNALUs] == 6)
    {
        WebRtc_UWord32 seiPayloadSize;
        do
        {
            // SEI message
            seiPayloadSize = 0;

            WebRtc_UWord32 curByte = _info.startCodeSize[_info.numNALUs] + 1;
            const WebRtc_UWord32 seiStartOffset = curByte;

            WebRtc_UWord32 seiPayloadType = 0;
            while(_ptrData[curByte] == 0xff)
            {
                seiPayloadType += 255;
                curByte++;
            }
            seiPayloadType += _ptrData[curByte++];

            while(_ptrData[curByte] == 0xff)
            {
                seiPayloadSize += 255;
                curByte++;
            }
            seiPayloadSize += _ptrData[curByte++];

            if(_info.payloadSize[_info.numNALUs] < _info.startCodeSize[_info.numNALUs] + seiPayloadSize)
            {
                // sanity of remaining buffer
                // return 0 since no one "need" SEI messages
                assert(false);
               return 0;
            }

            if(seiPayloadType == 24)
            {
                // we add this to NALU 0 to be signaled in the first PACSI packet
                _info.PACSI[0].numSEINALUs = 1; // we allways add this to NALU 0 to send it in the first packet
                if(_info.PACSI[0].seiMessageLength[0] != seiPayloadSize)
                {
                    _info.PACSI[0].seiMessageLength[0] = seiPayloadSize;
                    delete [] _info.PACSI[0].seiMessageData[0];
                    _info.PACSI[0].seiMessageData[0] = new WebRtc_UWord8[seiPayloadSize];
                }
                memcpy(_info.PACSI[0].seiMessageData[0], _ptrData+seiStartOffset, seiPayloadSize);

                _info.PACSI[0].NALlength += seiPayloadSize + 2; // additional 2 is the length

#ifdef DEBUG_SEI_MESSAGE
                const WebRtc_UWord8 numberOfLayers = 10;
                WebRtc_UWord16 avgBitrate[numberOfLayers]= {0,0,0,0,0,0,0,0,0,0};
                WebRtc_UWord16 maxBitrateLayer[numberOfLayers]= {0,0,0,0,0,0,0,0,0,0};
                WebRtc_UWord16 maxBitrateLayerRepresentation[numberOfLayers] = {0,0,0,0,0,0,0,0,0,0};
                WebRtc_UWord16 maxBitrareCalcWindow[numberOfLayers] = {0,0,0,0,0,0,0,0,0,0};

                BitstreamParser parserScalabilityInfo(_ptrData+curByte, seiPayloadSize);

                parserScalabilityInfo.Get1Bit(); // not used in futher parsing
                const WebRtc_UWord8 priority_layer_info_present = parserScalabilityInfo.Get1Bit();
                const WebRtc_UWord8 priority_id_setting_flag = parserScalabilityInfo.Get1Bit();

                WebRtc_UWord32 numberOfLayersMinusOne = parserScalabilityInfo.GetUE();
                for(WebRtc_UWord32 j = 0; j<= numberOfLayersMinusOne; j++)
                {
                    printf("\nLayer ID:%d \n",parserScalabilityInfo.GetUE());
                    printf("Priority ID:%d \n", parserScalabilityInfo.Get6Bits());
                    printf("Discardable:%d \n", parserScalabilityInfo.Get1Bit());

                    printf("Dependency ID:%d \n", parserScalabilityInfo.Get3Bits());
                    printf("Quality ID:%d \n", parserScalabilityInfo.Get4Bits());
                    printf("Temporal ID:%d \n", parserScalabilityInfo.Get3Bits());

                    const WebRtc_UWord8 sub_pic_layer_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 sub_region_layer_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 iroi_division_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 profile_level_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 bitrate_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 frm_rate_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 frm_size_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 layer_dependency_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 parameter_sets_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 bitstream_restriction_info_present_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 exact_inter_layer_pred_flag = parserScalabilityInfo.Get1Bit();  // not used in futher parsing

                    if(sub_pic_layer_flag || iroi_division_info_present_flag)
                    {
                        parserScalabilityInfo.Get1Bit();
                    }
                    const WebRtc_UWord8 layer_conversion_flag = parserScalabilityInfo.Get1Bit();
                    const WebRtc_UWord8 layer_output_flag = parserScalabilityInfo.Get1Bit();  // not used in futher parsing

                    if(profile_level_info_present_flag)
                    {
                        parserScalabilityInfo.Get24Bits();
                    }
                    if(bitrate_info_present_flag)
                    {
                        // this is what we want
                        avgBitrate[j] = parserScalabilityInfo.Get16Bits();
                        maxBitrateLayer[j] = parserScalabilityInfo.Get16Bits();
                        maxBitrateLayerRepresentation[j] = parserScalabilityInfo.Get16Bits();
                        maxBitrareCalcWindow[j] = parserScalabilityInfo.Get16Bits();

                        printf("\tAvg:%d\n", BitRateBPS(avgBitrate[j]));
                        printf("\tmaxBitrate:%d\n", BitRateBPS(maxBitrateLayer[j]));
                        printf("\tmaxBitrate rep:%d\n", BitRateBPS(maxBitrateLayerRepresentation[j]));
                        printf("\tCalcWindow:%d\n", maxBitrareCalcWindow[j]);
                    }
                    if(frm_rate_info_present_flag)
                    {
                        printf("\tFrame rate constant:%d\n", parserScalabilityInfo.Get2Bits()); // 0 = not constant, 1 = constant, 2 = maybe...
                        printf("\tFrame rate avg:%d\n", parserScalabilityInfo.Get16Bits()/256);
                    }
                    if(frm_size_info_present_flag || iroi_division_info_present_flag)
                    {
                        printf("\tFrame Width:%d\n",(parserScalabilityInfo.GetUE()+1)*16);
                        printf("\tFrame Height:%d\n",(parserScalabilityInfo.GetUE()+1)*16);
                    }
                    if(sub_region_layer_flag)
                    {
                        parserScalabilityInfo.GetUE();
                        if(parserScalabilityInfo.Get1Bit())
                        {
                            parserScalabilityInfo.Get16Bits();
                            parserScalabilityInfo.Get16Bits();
                            parserScalabilityInfo.Get16Bits();
                            parserScalabilityInfo.Get16Bits();
                        }
                    }
                    if(sub_pic_layer_flag)
                    {
                        parserScalabilityInfo.GetUE();
                    }
                    if(iroi_division_info_present_flag)
                    {
                        if(parserScalabilityInfo.Get1Bit())
                        {
                            parserScalabilityInfo.GetUE();
                            parserScalabilityInfo.GetUE();
                        }else
                        {
                            const WebRtc_UWord32 numRoisMinusOne = parserScalabilityInfo.GetUE();
                            for(WebRtc_UWord32 k = 0; k <= numRoisMinusOne; k++)
                            {
                                parserScalabilityInfo.GetUE();
                                parserScalabilityInfo.GetUE();
                                parserScalabilityInfo.GetUE();
                            }
                        }
                    }
                    if(layer_dependency_info_present_flag)
                    {
                        const WebRtc_UWord32 numDirectlyDependentLayers = parserScalabilityInfo.GetUE();
                        for(WebRtc_UWord32 k = 0; k < numDirectlyDependentLayers; k++)
                        {
                            parserScalabilityInfo.GetUE();
                        }
                    } else
                    {
                        parserScalabilityInfo.GetUE();
                    }
                    if(parameter_sets_info_present_flag)
                    {
                        const WebRtc_UWord32 numSeqParameterSetMinusOne = parserScalabilityInfo.GetUE();
                        for(WebRtc_UWord32 k = 0; k <= numSeqParameterSetMinusOne; k++)
                        {
                            parserScalabilityInfo.GetUE();
                        }
                        const WebRtc_UWord32 numSubsetSeqParameterSetMinusOne = parserScalabilityInfo.GetUE();
                        for(WebRtc_UWord32 l = 0; l <= numSubsetSeqParameterSetMinusOne; l++)
                        {
                            parserScalabilityInfo.GetUE();
                        }
                        const WebRtc_UWord32 numPicParameterSetMinusOne = parserScalabilityInfo.GetUE();
                        for(WebRtc_UWord32 m = 0; m <= numPicParameterSetMinusOne; m++)
                        {
                            parserScalabilityInfo.GetUE();
                        }
                    }else
                    {
                        parserScalabilityInfo.GetUE();
                    }
                    if(bitstream_restriction_info_present_flag)
                    {
                        parserScalabilityInfo.Get1Bit();
                        parserScalabilityInfo.GetUE();
                        parserScalabilityInfo.GetUE();
                        parserScalabilityInfo.GetUE();
                        parserScalabilityInfo.GetUE();
                        parserScalabilityInfo.GetUE();
                        parserScalabilityInfo.GetUE();
                    }
                    if(layer_conversion_flag)
                    {
                        parserScalabilityInfo.GetUE();
                        for(WebRtc_UWord32 k = 0; k <2;k++)
                        {
                            if(parserScalabilityInfo.Get1Bit())
                            {
                                parserScalabilityInfo.Get24Bits();
                                parserScalabilityInfo.Get16Bits();
                                parserScalabilityInfo.Get16Bits();
                            }
                        }
                    }
                }
                if(priority_layer_info_present)
                {
                    const WebRtc_UWord32 prNumDidMinusOne = parserScalabilityInfo.GetUE();
                    for(WebRtc_UWord32 k = 0; k <= prNumDidMinusOne;k++)
                    {
                        parserScalabilityInfo.Get3Bits();
                        const WebRtc_UWord32 prNumMinusOne = parserScalabilityInfo.GetUE();
                        for(WebRtc_UWord32 l = 0; l <= prNumMinusOne; l++)
                        {
                            parserScalabilityInfo.GetUE();
                            parserScalabilityInfo.Get24Bits();
                            parserScalabilityInfo.Get16Bits();
                            parserScalabilityInfo.Get16Bits();
                        }
                    }
                }
                if(priority_id_setting_flag)
                {
                    WebRtc_UWord8 priorityIdSettingUri;
                    WebRtc_UWord32 priorityIdSettingUriIdx = 0;
                    do
                    {
                        priorityIdSettingUri = parserScalabilityInfo.Get8Bits();
                    } while (priorityIdSettingUri != 0);
                }
#endif
            } else
            {
                // not seiPayloadType 24 ignore
            }
            //check if we have more SEI in NALU
        } while (_info.payloadSize[_info.numNALUs] > _info.startCodeSize[_info.numNALUs] + seiPayloadSize);
    }

   // Extended NAL unit header (3 bytes).
   // +---------------+---------------+---------------+
   // |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   // |R|I|   PRID    |N| DID |  QID  | TID |U|D|O| RR|
   // +---------------+---------------+---------------+

   // R    - Reserved for future extensions (MUST be 1). Receivers SHOULD ignore the value of R.
   // I    - Is layer representation an IDR layer (1) or not (0).
   // PRID - Priority identifier for the NAL unit.
   // N    - Specifies whether inter-layer prediction may be used for decoding the coded slice (1) or not (0).
   // DID  - Indicates the inter-layer coding dependency level of a layer representation.
   // QID  - Indicates the quality level of an MGS layer representation.
   // TID  - Indicates the temporal level of a layer representation.
   // U    - Use only reference base pictures during the inter prediction process (1) or not (0).
   // D    - Discardable flag.
   // O    - Output_flag. Affects the decoded picture output process as defined in Annex C of [H.264].
   // RR   - Reserved_three_2bits (MUST be '11'). Receivers SHOULD ignore the value of RR.

    if (_info.type[_info.numNALUs] == 14 ||
        _info.type[_info.numNALUs] == 20)
    {
        WebRtc_UWord32 curByte = _info.startCodeSize[_info.numNALUs] + 1;

        if (_remLength < curByte + 3)
        {
                return -1;
        }

        _info.SVCheader[_info.numNALUs].idr        = (_ptrData[curByte] >> 6) & 0x01;
        _info.SVCheader[_info.numNALUs].priorityID = (_ptrData[curByte++] & 0x3F);

        _info.SVCheader[_info.numNALUs].interLayerPred = (_ptrData[curByte] >> 7) & 0x01;
        _info.SVCheader[_info.numNALUs].dependencyID   = (_ptrData[curByte] >> 4) & 0x07;
        _info.SVCheader[_info.numNALUs].qualityID      = (_ptrData[curByte++] & 0x0F);

        _info.SVCheader[_info.numNALUs].temporalID     = (_ptrData[curByte] >> 5) & 0x07;
        _info.SVCheader[_info.numNALUs].useRefBasePic  = (_ptrData[curByte] >> 4) & 0x01;
        _info.SVCheader[_info.numNALUs].discardable    = (_ptrData[curByte] >> 3) & 0x01;
        _info.SVCheader[_info.numNALUs].output         = (_ptrData[curByte] >> 2) & 0x01;

        if (_info.type[_info.numNALUs] == 14)
        {
            // inform the next NALU
            memcpy(&(_info.SVCheader[_info.numNALUs+1]), &(_info.SVCheader[_info.numNALUs]), sizeof(_H264_SVC_NALUHeader));
        }
    }
   return 0;
}


/*******************************************************************************
 * void SetLayerSEBit();
 *
 * Sets start and end bits for the current NAL unit.
 *
 * Output:
 *          - _info.PACSI[currentNALU].S    : First NAL unit in a layer (S = 1).
 *          - _info.PACSI[currentNALU].E    : Last NAL unit in a layer (E = 1).
 *
 */
void
H264Information::SetLayerSEBit(WebRtc_Word32 foundLast)
{
    if (_info.numNALUs == 0)
    {
        // First NAL unit
        _info.PACSI[_info.numNALUs].S = 1;
    }

    if (_info.numNALUs > 0)
    {
        if (_info.type[_info.numNALUs] != _info.type[_info.numNALUs-1] &&
           (_info.type[_info.numNALUs] == 20))
        {
            // First layer in scalable extension
            _info.PACSI[_info.numNALUs].S   = 1;
            _info.PACSI[_info.numNALUs-1].E = 1;
        }

        if (_info.type[_info.numNALUs] == 20 && _info.type[_info.numNALUs-1] == 20)
        {
            if (_info.SVCheader[_info.numNALUs].temporalID   != _info.SVCheader[_info.numNALUs-1].temporalID ||
                _info.SVCheader[_info.numNALUs].dependencyID != _info.SVCheader[_info.numNALUs-1].dependencyID ||
                _info.SVCheader[_info.numNALUs].qualityID    != _info.SVCheader[_info.numNALUs-1].qualityID)
            {
                // New layer in scalable extension
                _info.PACSI[_info.numNALUs].S   = 1;
                _info.PACSI[_info.numNALUs-1].E = 1;
            }
        }
    }

    if (foundLast)
    {
        // Last NAL unit
        _info.PACSI[_info.numNALUs].E = 1;
    }

}

/*******************************************************************************
 * WebRtc_Word32 SetLayerLengths();
 *
 * Sets the accumulated layer length.
 *
 * Output:
 *          - _info.accLayerSize[currentLayer]   : Size in bytes of layer: 0 - currentLayer.
 *
 * Return value:
 *          - 0                        : ok
 *          - (-1)                     : Error
 *
 */
WebRtc_Word32
H264Information::SetLayerLengths()
{
    for (WebRtc_UWord32 curNALU = 0; curNALU < _info.numNALUs; curNALU++)
    {
        _info.accLayerSize[_info.numLayers] += _info.startCodeSize[curNALU] + _info.payloadSize[curNALU];

        if (_info.PACSI[curNALU].E == 1)
        {
            _info.numLayers++;
            if (curNALU == WebRtc_UWord32(_info.numNALUs - 1))
            {
                break;
            }
            if (_info.numLayers >= KMaxNumberOfLayers)
            {
                Reset();
                return -1;
            }
            _info.accLayerSize[_info.numLayers] += _info.accLayerSize[_info.numLayers - 1];
        }
    }

    if (_info.numLayers < 1 && _info.numLayers > KMaxNumberOfLayers)
    {
        Reset();
        return -1;
    }

    if (_info.accLayerSize[_info.numLayers - 1] != WebRtc_Word32(_length))
    {
        Reset();
        return -1;
    }

    return 0;
}
} // namespace webrtc
