/*
 *  Copyright (c) 2012 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 <math.h>
#include <stdio.h>

#include "common_types.h"
#include "../source/event.h"
#include "frame_buffer.h"
#include "inter_frame_delay.h"
#include "jitter_buffer.h"
#include "jitter_estimate_test.h"
#include "jitter_estimator.h"
#include "media_opt_util.h"
#include "modules/video_coding/main/source/tick_time_base.h"
#include "packet.h"
#include "test_util.h"
#include "test_macros.h"

// TODO(holmer): Get rid of this to conform with style guide.
using namespace webrtc;

// TODO (Mikhal/Stefan): Update as gtest and separate to specific tests.

int CheckOutFrame(VCMEncodedFrame* frameOut, unsigned int size, bool startCode)
{
    if (frameOut == 0)
    {
        return -1;
    }

    const WebRtc_UWord8* outData = frameOut->Buffer();

    unsigned int i = 0;

    if(startCode)
    {
        if (outData[0] != 0 || outData[1] != 0 || outData[2] != 0 ||
            outData[3] != 1)
        {
            return -2;
        }
        i+= 4;
    }

    // check the frame data
    int count = 3;

    // check the frame length
    if (frameOut->Length() != size)
    {
        return -3;
    }

    for(; i < size; i++)
    {
        if (outData[i] == 0 && outData[i + 1] == 0 && outData[i + 2] == 0x80)
        {
            i += 2;
        }
        else if(startCode && outData[i] == 0 && outData[i + 1] == 0)
        {
            if (outData[i] != 0 || outData[i + 1] != 0 ||
                outData[i + 2] != 0 || outData[i + 3] != 1)
            {
                return -3;
            }
            i += 3;
        }
        else
        {
            if (outData[i] != count)
            {
                return -4;
            }
            count++;
            if(count == 10)
            {
                count = 3;
            }
        }
    }
    return 0;
}


int JitterBufferTest(CmdArgs& args)
{
    // Don't run these tests with debug event.
#if defined(EVENT_DEBUG)
    return -1;
#endif
    TickTimeBase clock;

    // Start test
    WebRtc_UWord16 seqNum = 1234;
    WebRtc_UWord32 timeStamp = 0;
    int size = 1400;
    WebRtc_UWord8 data[1500];
    VCMPacket packet(data, size, seqNum, timeStamp, true);

    VCMJitterBuffer jb(&clock);

    seqNum = 1234;
    timeStamp = 123*90;
    FrameType incomingFrameType(kVideoFrameKey);
    VCMEncodedFrame* frameOut=NULL;
    WebRtc_Word64 renderTimeMs = 0;
    packet.timestamp = timeStamp;
    packet.seqNum = seqNum;

    // build a data vector with 0, 0, 0x80, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0x80, 3....
    data[0] = 0;
    data[1] = 0;
    data[2] = 0x80;
    int count = 3;
    for (unsigned int i = 3; i < sizeof(data) - 3; ++i)
    {
        data[i] = count;
        count++;
        if(count == 10)
        {
            data[i+1] = 0;
            data[i+2] = 0;
            data[i+3] = 0x80;
            count = 3;
            i += 3;
        }
    }

    // Not started
    TEST(0 == jb.GetFrame(packet));
    TEST(-1 == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
    TEST(0 == jb.GetCompleteFrameForDecoding(10));
    TEST(0 == jb.GetFrameForDecoding());

    // Start
    jb.Start();

    // Get frame to use for this timestamp
    VCMEncodedFrame* frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // No packets inserted
    TEST(0 == jb.GetCompleteFrameForDecoding(10));


    //
    // TEST single packet frame
    //
    //  --------
    // |  1234  |
    //  --------

    // packet.frameType;
    // packet.dataPtr;
    // packet.sizeBytes;
    // packet.timestamp;
    // packet.seqNum;
    // packet.isFirstPacket;
    // packet.markerBit;
    //
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE delta frame 1 packet\n");

    //
    // TEST dual packet frame
    //
    //  -----------------
    // |  1235  |  1236  |
    //  -----------------
    //

    seqNum++;
    timeStamp += 33*90;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*2, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE delta frame 2 packets\n");


    //
    // TEST 100 packets frame Key frame
    //
    //  ----------------------------------
    // |  1237  |  1238  |  .... |  1336  |
    //  ----------------------------------

    // insert first packet
    timeStamp += 33*90;
    seqNum++;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameKey);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    // insert 98 frames
    int loop = 0;
    do
    {
        seqNum++;
        packet.isFirstPacket = false;
        packet.markerBit = false;
        packet.seqNum = seqNum;

        frameIn = jb.GetFrame(packet);
        TEST(frameIn != 0);

        // Insert a packet into a frame
        TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
        loop++;
    } while (loop < 98);

    // insert last packet
    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*100, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameKey);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE key frame 100 packets\n");

    //
    // TEST 100 packets frame Delta frame
    //
    //  ----------------------------------
    // |  1337  |  1238  |  .... |  1436  |
    //  ----------------------------------

    // insert first packet
    timeStamp += 33*90;
    seqNum++;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    // insert 98 frames
    loop = 0;
    do
    {
        seqNum++;
        packet.isFirstPacket = false;
        packet.markerBit = false;
        packet.seqNum = seqNum;

        frameIn = jb.GetFrame(packet);
        TEST(frameIn != 0);

        // Insert a packet into a frame
        TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
        loop++;
    } while (loop < 98);

    // insert last packet
    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*100, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE delta frame 100 packets\n");

    //
    // TEST packet re-ordering reverse order
    //
    //  ----------------------------------
    // |  1437  |  1238  |  .... |  1536  |
    //  ----------------------------------
    //            <----------

    // insert "first" packet last seqnum
    timeStamp += 33*90;
    seqNum += 100;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    // insert 98 packets
    loop = 0;
    do
    {
        seqNum--;
        packet.isFirstPacket = false;
        packet.markerBit = false;
        packet.seqNum = seqNum;

        frameIn = jb.GetFrame(packet);
        TEST(frameIn != 0);

        // Insert a packet into a frame
        TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
        loop++;
    } while (loop < 98);

    // insert last packet
    seqNum--;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*100, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE delta frame 100 packets reverse order\n");

    seqNum+= 100;

    //
    // TEST frame re-ordering 2 frames 2 packets each
    //
    //  -----------------     -----------------
    // |  1539  |  1540  |   |  1537  |  1538  |
    //  -----------------     -----------------

    seqNum += 2;
    timeStamp += 2* 33 * 90;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // check that we fail to get frame since seqnum is not continuous
    frameOut = jb.GetCompleteFrameForDecoding(10);
    TEST(frameOut == 0);

    seqNum -= 3;
    timeStamp -= 33*90;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*2, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*2, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    seqNum += 2;
    //printf("DONE frame re-ordering 2 frames 2 packets\n");

    // restore
    packet.dataPtr = data;
    packet.codec = kVideoCodecUnknown;

    //
    // TEST duplicate packets
    //
    //  -----------------
    // |  1543  |  1543  |
    //  -----------------
    //

   seqNum++;
    timeStamp += 2*33*90;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    packet.isFirstPacket = false;
    packet.markerBit = true;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kDuplicatePacket == jb.InsertPacket(frameIn, packet));

    seqNum++;
    packet.seqNum = seqNum;

    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*2, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE test duplicate packets\n");

    //
    // TEST H.264 insert start code
    //
    //  -----------------
    // |  1544  |  1545  |
    //  -----------------
    // insert start code, both packets

    seqNum++;
    timeStamp += 33 * 90;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.insertStartCode = true;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size * 2 + 4 * 2, true) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    // reset
    packet.insertStartCode = false;
    //printf("DONE H.264 insert start code test 2 packets\n");

    //
    // TEST statistics
    //
    WebRtc_UWord32 numDeltaFrames = 0;
    WebRtc_UWord32 numKeyFrames = 0;
    jb.FrameStatistics(&numDeltaFrames, &numKeyFrames);

    TEST(numDeltaFrames == 8);
    TEST(numKeyFrames == 1);

    WebRtc_UWord32 frameRate;
    WebRtc_UWord32 bitRate;
    jb.IncomingRateStatistics(&frameRate, &bitRate);

    // these depend on CPU speed works on a T61
    TEST(frameRate > 30);
    TEST(bitRate > 10000000);


    jb.Flush();

    //
    // TEST packet loss. Verify missing packets statistics and not decodable
    // packets statistics.
    // Insert 10 frames consisting of 4 packets and remove one from all of them.
    // The last packet is an empty (non-media) packet
    //

    // Select a start seqNum which triggers a difficult wrap situation
    // The JB will only output (incomplete)frames if the next one has started
    // to arrive. Start by inserting one frame (key).
    seqNum = 0xffff - 4;
    seqNum++;
    timeStamp += 33*90;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.completeNALU = kNaluStart;
    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);
    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    for (int i = 0; i < 11; ++i) {
      webrtc::FrameType frametype = kVideoFrameDelta;
      seqNum++;
      timeStamp += 33*90;
      packet.frameType = frametype;
      packet.isFirstPacket = true;
      packet.markerBit = false;
      packet.seqNum = seqNum;
      packet.timestamp = timeStamp;
      packet.completeNALU = kNaluStart;

      frameIn = jb.GetFrame(packet);
      TEST(frameIn != 0);

      // Insert a packet into a frame
      TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

      // Get packet notification
      TEST(timeStamp - 33 * 90 == jb.NextTimestamp(10, &incomingFrameType,
                                                   &renderTimeMs));

      // Check incoming frame type
      if (i == 0)
      {
          TEST(incomingFrameType == kVideoFrameKey);
      }
      else
      {
          TEST(incomingFrameType == frametype);
      }

      // Get the frame
      frameOut = jb.GetCompleteFrameForDecoding(10);

      // Should not be complete
      TEST(frameOut == 0);

      seqNum += 2;
      packet.isFirstPacket = false;
      packet.markerBit = true;
      packet.seqNum = seqNum;
      packet.completeNALU = kNaluEnd;

      frameIn = jb.GetFrame(packet);
      TEST(frameIn != 0);

      // Insert a packet into a frame
      TEST(kIncomplete == jb.InsertPacket(frameIn, packet));


      // Insert an empty (non-media) packet
      seqNum++;
      packet.isFirstPacket = false;
      packet.markerBit = false;
      packet.seqNum = seqNum;
      packet.completeNALU = kNaluEnd;
      packet.frameType = kFrameEmpty;

      frameIn = jb.GetFrame(packet);
      TEST(frameIn != 0);

      // Insert a packet into a frame
      TEST(kIncomplete == jb.InsertPacket(frameIn, packet));

      // Get the frame
      frameOut = jb.GetFrameForDecoding();

      // One of the packets has been discarded by the jitter buffer.
      // Last frame can't be extracted yet.
      if (i < 10)
      {
          TEST(CheckOutFrame(frameOut, size, false) == 0);

          // check the frame type
          if (i == 0)
          {
              TEST(frameOut->FrameType() == kVideoFrameKey);
          }
         else
         {
             TEST(frameOut->FrameType() == frametype);
         }
          TEST(frameOut->Complete() == false);
          TEST(frameOut->MissingFrame() == false);
      }

      // Release frame (when done with decoding)
      jb.ReleaseFrame(frameOut);
    }

    TEST(jb.num_not_decodable_packets() == 10);

    // Insert 3 old packets and verify that we have 3 discarded packets
    // Match value to actual latest timestamp decoded
    timeStamp -= 33 * 90;
    packet.timestamp = timeStamp - 1000;
    frameIn = jb.GetFrame(packet);
    TEST(frameIn == NULL);

    packet.timestamp = timeStamp - 500;
    frameIn = jb.GetFrame(packet);
    TEST(frameIn == NULL);

    packet.timestamp = timeStamp - 100;
    frameIn = jb.GetFrame(packet);
    TEST(frameIn == NULL);

    TEST(jb.num_discarded_packets() == 3);

    jb.Flush();

    // This statistic shouldn't be reset by a flush.
    TEST(jb.num_discarded_packets() == 3);

    //printf("DONE Statistics\n");


    // Temporarily do this to make the rest of the test work:
    timeStamp += 33*90;
    seqNum += 4;


    //
    // TEST delta frame 100 packets with seqNum wrap
    //
    //  ---------------------------------------
    // |  65520  |  65521  | ... |  82  |  83  |
    //  ---------------------------------------
    //

    jb.Flush();

    // insert first packet
    timeStamp += 33*90;
    seqNum = 0xfff0;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    // insert 98 packets
    loop = 0;
    do
    {
        seqNum++;
        packet.isFirstPacket = false;
        packet.markerBit = false;
        packet.seqNum = seqNum;

        frameIn = jb.GetFrame(packet);
        TEST(frameIn != 0);

        // Insert a packet into a frame
        TEST(kIncomplete == jb.InsertPacket(frameIn, packet));

        // get packet notification
        TEST(timeStamp == jb.NextTimestamp(2, &incomingFrameType,
                                           &renderTimeMs));

        // check incoming frame type
        TEST(incomingFrameType == kVideoFrameDelta);

        // get the frame
        frameOut = jb.GetCompleteFrameForDecoding(2);

        // it should not be complete
        TEST(frameOut == 0);

        loop++;
    } while (loop < 98);

    // insert last packet
    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*100, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE delta frame 100 packets with wrap in seqNum\n");

    //
    // TEST packet re-ordering reverse order with neg seqNum warp
    //
    //  ----------------------------------------
    // |  65447  |  65448  | ... |   9   |  10  |
    //  ----------------------------------------
    //              <-------------

    // test flush
    jb.Flush();

    // insert "first" packet last seqnum
    timeStamp += 33*90;
    seqNum = 10;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    // insert 98 frames
    loop = 0;
    do
    {
        seqNum--;
        packet.isFirstPacket = false;
        packet.markerBit = false;
        packet.seqNum = seqNum;

        frameIn = jb.GetFrame(packet);
        TEST(frameIn != 0);

        // Insert a packet into a frame
        TEST(kIncomplete == jb.InsertPacket(frameIn, packet));

        // get packet notification
        TEST(timeStamp == jb.NextTimestamp(2, &incomingFrameType,
                                           &renderTimeMs));

        // check incoming frame type
        TEST(incomingFrameType == kVideoFrameDelta);

        // get the frame
        frameOut = jb.GetCompleteFrameForDecoding(2);

        // it should not be complete
        TEST(frameOut == 0);

        loop++;
    } while (loop < 98);

    // insert last packet
    seqNum--;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*100, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE delta frame 100 packets reverse order with wrap in seqNum \n");

    // test flush
    jb.Flush();

    //
    // TEST packet re-ordering with seqNum wrap
    //
    //  -----------------------
    // |   1   | 65535 |   0   |
    //  -----------------------

    // insert "first" packet last seqnum
    timeStamp += 33*90;
    seqNum = 1;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    // insert last packet
    seqNum -= 2;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = false;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*3, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE delta frame 3 packets re-ordering with wrap in seqNum \n");

    // test flush
    jb.Flush();

    //
    // TEST insert old frame
    //
    //   -------      -------
    //  |   2   |    |   1   |
    //   -------      -------
    //  t = 3000     t = 2000

    seqNum = 2;
    timeStamp = 3000;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(3000 == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);
    TEST(3000 == frameOut->TimeStamp());

    TEST(CheckOutFrame(frameOut, size, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    jb.ReleaseFrame(frameOut);

    seqNum--;
    timeStamp = 2000;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    // Changed behavior, never insert packets into frames older than the
    // last decoded frame.
    TEST(frameIn == 0);

    //printf("DONE insert old frame\n");

    jb.Flush();

   //
    // TEST insert old frame with wrap in timestamp
    //
    //   -------      -------
    //  |   2   |    |   1   |
    //   -------      -------
    //  t = 3000     t = 0xffffff00

    seqNum = 2;
    timeStamp = 3000;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);
    TEST(timeStamp == frameOut->TimeStamp());

    TEST(CheckOutFrame(frameOut, size, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    jb.ReleaseFrame(frameOut);

    seqNum--;
    timeStamp = 0xffffff00;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    // This timestamp is old
    TEST(frameIn == 0);

    jb.Flush();

    //
    // TEST wrap in timeStamp
    //
    //  ---------------     ---------------
    // |   1   |   2   |   |   3   |   4   |
    //  ---------------     ---------------
    //  t = 0xffffff00        t = 33*90

    seqNum = 1;
    timeStamp = 0xffffff00;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*2, false) == 0);

    jb.ReleaseFrame(frameOut);

    seqNum++;
    timeStamp += 33*90;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = false;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameDelta);

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    // it should not be complete
    TEST(frameOut == 0);

    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kCompleteSession == jb.InsertPacket(frameIn, packet));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);

    TEST(CheckOutFrame(frameOut, size*2, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    //printf("DONE time stamp wrap 2 frames 2 packets\n");

    jb.Flush();

    //
    // TEST insert 2 frames with wrap in timeStamp
    //
    //   -------          -------
    //  |   1   |        |   2   |
    //   -------          -------
    // t = 0xffffff00    t = 2700

    seqNum = 1;
    timeStamp = 0xffffff00;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert first frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // Get packet notification
    TEST(0xffffff00 == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Insert next frame
    seqNum++;
    timeStamp = 2700;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // Get packet notification
    TEST(0xffffff00 == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Get frame
    frameOut = jb.GetCompleteFrameForDecoding(10);
    TEST(0xffffff00 == frameOut->TimeStamp());

    TEST(CheckOutFrame(frameOut, size, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // Get packet notification
    TEST(2700 == jb.NextTimestamp(0, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Get frame
    VCMEncodedFrame* frameOut2 = jb.GetCompleteFrameForDecoding(10);
    TEST(2700 == frameOut2->TimeStamp());

    TEST(CheckOutFrame(frameOut2, size, false) == 0);

    // check the frame type
    TEST(frameOut2->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);
    jb.ReleaseFrame(frameOut2);

    //printf("DONE insert 2 frames (1 packet) with wrap in timestamp\n");

    jb.Flush();

    //
    // TEST insert 2 frames re-ordered with wrap in timeStamp
    //
    //   -------          -------
    //  |   2   |        |   1   |
    //   -------          -------
    //  t = 2700        t = 0xffffff00

    seqNum = 2;
    timeStamp = 2700;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert first frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // Get packet notification
    TEST(2700 == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Insert second frame
    seqNum--;
    timeStamp = 0xffffff00;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // Get packet notification
    TEST(0xffffff00 == jb.NextTimestamp(10, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Get frame
    frameOut = jb.GetCompleteFrameForDecoding(10);
    TEST(0xffffff00 == frameOut->TimeStamp());

    TEST(CheckOutFrame(frameOut, size, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameDelta);

    // get packet notification
    TEST(2700 == jb.NextTimestamp(0, &incomingFrameType, &renderTimeMs));
    TEST(kVideoFrameDelta == incomingFrameType);

    // Get frame
    frameOut2 = jb.GetCompleteFrameForDecoding(10);
    TEST(2700 == frameOut2->TimeStamp());

    TEST(CheckOutFrame(frameOut2, size, false) == 0);

    // check the frame type
    TEST(frameOut2->FrameType() == kVideoFrameDelta);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);
    jb.ReleaseFrame(frameOut2);

    //printf("DONE insert 2 frames (1 packet) re-ordered with wrap in timestamp\n");

    //
    // TEST delta frame with more than max number of packets
    //

    jb.Start();

    loop = 0;
    packet.timestamp += 33*90;
    bool firstPacket = true;
    // insert kMaxPacketsInJitterBuffer into frame
    do
    {
        seqNum++;
        packet.isFirstPacket = false;
        packet.markerBit = false;
        packet.seqNum = seqNum;

        frameIn = jb.GetFrame(packet);
        TEST(frameIn != 0);

        // Insert frame
        if (firstPacket)
        {
            TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
            firstPacket = false;
        }
        else
        {
            TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
        }

        // get packet notification
        TEST(packet.timestamp == jb.NextTimestamp(10, &incomingFrameType,
                                                  &renderTimeMs));

        // check incoming frame type
        TEST(incomingFrameType == kVideoFrameDelta);

        loop++;
    } while (loop < kMaxPacketsInSession);

    // Max number of packets inserted

    // Insert one more packet
    seqNum++;
    packet.isFirstPacket = false;
    packet.markerBit = true;
    packet.seqNum = seqNum;

    frameIn = jb.GetFrame(packet);
    TEST(frameIn != 0);

    // Insert the packet -> frame recycled
    TEST(kSizeError == jb.InsertPacket(frameIn, packet));

    TEST(0 == jb.GetCompleteFrameForDecoding(10));

    //printf("DONE fill frame - packets > max number of packets\n");

    //
    // TEST fill JB with more than max number of frame (50 delta frames +
    // 51 key frames) with wrap in seqNum
    //
    //  --------------------------------------------------------------
    // | 65485 | 65486 | 65487 | .... | 65535 | 0 | 1 | 2 | .....| 50 |
    //  --------------------------------------------------------------
    // |<-----------delta frames------------->|<------key frames----->|

    jb.Flush();

    loop = 0;
    seqNum = 65485;
    WebRtc_UWord32 timeStampStart = timeStamp +  33*90;
    WebRtc_UWord32 timeStampFirstKey = 0;
    VCMEncodedFrame* ptrLastDeltaFrame = NULL;
    VCMEncodedFrame* ptrFirstKeyFrame = NULL;
    // insert MAX_NUMBER_OF_FRAMES frames
    do
    {
        timeStamp += 33*90;
        seqNum++;
        packet.isFirstPacket = true;
        packet.markerBit = true;
        packet.seqNum = seqNum;
        packet.timestamp = timeStamp;

        frameIn = jb.GetFrame(packet);
        TEST(frameIn != 0);

        if (loop == 49)  // last delta
        {
            ptrLastDeltaFrame = frameIn;
        }
        if (loop == 50)  // first key
        {
            ptrFirstKeyFrame = frameIn;
            packet.frameType = kVideoFrameKey;
            timeStampFirstKey = packet.timestamp;
        }

        // Insert frame
        TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

        // Get packet notification, should be first inserted frame
        TEST(timeStampStart == jb.NextTimestamp(10, &incomingFrameType,
                                                &renderTimeMs));

        // check incoming frame type
        TEST(incomingFrameType == kVideoFrameDelta);

        loop++;
    } while (loop < kMaxNumberOfFrames);

    // Max number of frames inserted

    // Insert one more frame
    timeStamp += 33*90;
    seqNum++;
    packet.isFirstPacket = true;
    packet.markerBit = true;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;

    // Now, no free frame - frames will be recycled until first key frame
    frameIn = jb.GetFrame(packet);
    // ptr to last inserted delta frame should be returned
    TEST(frameIn != 0 && frameIn && ptrLastDeltaFrame);

    // Insert frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // First inserted key frame should be oldest in buffer
    TEST(timeStampFirstKey == jb.NextTimestamp(10, &incomingFrameType,
                                               &renderTimeMs));

    // check incoming frame type
    TEST(incomingFrameType == kVideoFrameKey);

    // get the first key frame
    frameOut = jb.GetCompleteFrameForDecoding(10);
    TEST(ptrFirstKeyFrame == frameOut);

    TEST(CheckOutFrame(frameOut, size, false) == 0);

    // check the frame type
    TEST(frameOut->FrameType() == kVideoFrameKey);

    // Release frame (when done with decoding)
    jb.ReleaseFrame(frameOut);

    jb.Flush();

    // printf("DONE fill JB - nr of delta + key frames (w/ wrap in seqNum) >
    // max nr of frames\n");

    // Testing that 1 empty packet inserted last will not be set for decoding
    seqNum = 3;
    // Insert one empty packet per frame, should never return the last timestamp
    // inserted. Only return empty frames in the presence of subsequent frames.
    int maxSize = 1000;
    for (int i = 0; i < maxSize + 10; i++)
    {
        timeStamp += 33 * 90;
        seqNum++;
        packet.isFirstPacket = false;
        packet.markerBit = false;
        packet.seqNum = seqNum;
        packet.timestamp = timeStamp;
        packet.frameType = kFrameEmpty;
        VCMEncodedFrame* testFrame = jb.GetFrameForDecoding();
        // timestamp should bever be the last TS inserted
        if (testFrame != NULL)
        {
            TEST(testFrame->TimeStamp() < timeStamp);
            printf("Not null TS = %d\n",testFrame->TimeStamp());
        }
    }

    jb.Flush();


    // printf(DONE testing inserting empty packets to the JB)


    // H.264 tests
    // Test incomplete NALU frames

    jb.Flush();
    jb.SetNackMode(kNoNack, -1, -1);
    seqNum ++;
    timeStamp += 33 * 90;
    int insertedLength = 0;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = true;
    packet.completeNALU = kNaluStart;
    packet.markerBit = false;

    frameIn = jb.GetFrame(packet);

     // Insert a packet into a frame
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    seqNum += 2; // Skip one packet
    packet.seqNum = seqNum;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = false;
    packet.completeNALU = kNaluIncomplete;
    packet.markerBit = false;

     // Insert a packet into a frame
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));

    seqNum++;
    packet.seqNum = seqNum;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = false;
    packet.completeNALU = kNaluEnd;
    packet.markerBit = false;

    // Insert a packet into a frame
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));

    seqNum++;
    packet.seqNum = seqNum;
    packet.completeNALU = kNaluComplete;
    packet.markerBit = true; // Last packet
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));


    // The JB will only output (incomplete) frames if a packet belonging to a
    // subsequent frame was already inserted. Insert one packet of a subsequent
    // frame. place high timestamp so the JB would always have a next frame
    // (otherwise, for every inserted frame we need to take care of the next
    // frame as well).
    packet.seqNum = 1;
    packet.timestamp = timeStamp + 33 * 90 * 10;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = false;
    packet.completeNALU = kNaluStart;
    packet.markerBit = false;
    frameIn = jb.GetFrame(packet);
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    // Get packet notification
    TEST(timeStamp == jb.NextTimestamp(10, &incomingFrameType,
                                       &renderTimeMs));
    frameOut = jb.GetFrameForDecoding();

    // We can decode everything from a NALU until a packet has been lost.
    // Thus we can decode the first packet of the first NALU and the second NALU
    // which consists of one packet.
    TEST(CheckOutFrame(frameOut, packet.sizeBytes * 2, false) == 0);
    jb.ReleaseFrame(frameOut);

    // Test reordered start frame + 1 lost
    seqNum += 2; // Reoreder 1 frame
    timeStamp += 33*90;
    insertedLength = 0;

    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = false;
    packet.completeNALU = kNaluEnd;
    packet.markerBit = false;

    TEST(frameIn = jb.GetFrame(packet));
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
    insertedLength += packet.sizeBytes; // This packet should be decoded

    seqNum--;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = true;
    packet.completeNALU = kNaluStart;
    packet.markerBit = false;
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
    insertedLength += packet.sizeBytes; // This packet should be decoded

    seqNum += 3; // One packet drop
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = false;
    packet.completeNALU = kNaluComplete;
    packet.markerBit = false;
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
    insertedLength += packet.sizeBytes; // This packet should be decoded

    seqNum += 1;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = false;
    packet.completeNALU = kNaluStart;
    packet.markerBit = false;
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
    // This packet should be decoded since it's the beginning of a NAL
    insertedLength += packet.sizeBytes;

    seqNum += 2;
    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = false;
    packet.completeNALU = kNaluEnd;
    packet.markerBit = true;
    TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
    // This packet should not be decoded because it is an incomplete NAL if it
    // is the last

    frameOut = jb.GetFrameForDecoding();
    // Only last NALU is complete
    TEST(CheckOutFrame(frameOut, insertedLength, false) == 0);
    jb.ReleaseFrame(frameOut);


    // Test to insert empty packet
    seqNum += 1;
    timeStamp += 33 * 90;
    VCMPacket emptypacket(data, 0, seqNum, timeStamp, true);
    emptypacket.seqNum = seqNum;
    emptypacket.timestamp = timeStamp;
    emptypacket.frameType = kVideoFrameKey;
    emptypacket.isFirstPacket = true;
    emptypacket.completeNALU = kNaluComplete;
    emptypacket.markerBit = true;
    TEST(frameIn = jb.GetFrame(emptypacket));
    TEST(kFirstPacket == jb.InsertPacket(frameIn, emptypacket));
    // This packet should not be decoded because it is an incomplete NAL if it
    // is the last
    insertedLength += 0;

    // Will be sent to the decoder, as a packet belonging to a subsequent frame
    // has arrived.
    frameOut = jb.GetFrameForDecoding();


    // Test that a frame can include an empty packet.
    seqNum += 1;
    timeStamp += 33 * 90;

    packet.seqNum = seqNum;
    packet.timestamp = timeStamp;
    packet.frameType = kVideoFrameKey;
    packet.isFirstPacket = true;
    packet.completeNALU = kNaluComplete;
    packet.markerBit = false;
    TEST(frameIn = jb.GetFrame(packet));
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    seqNum += 1;
    emptypacket.seqNum = seqNum;
    emptypacket.timestamp = timeStamp;
    emptypacket.frameType = kVideoFrameKey;
    emptypacket.isFirstPacket = true;
    emptypacket.completeNALU = kNaluComplete;
    emptypacket.markerBit = true;
    TEST(kCompleteSession == jb.InsertPacket(frameIn, emptypacket));

    // get the frame
    frameOut = jb.GetCompleteFrameForDecoding(10);
    // Only last NALU is complete
    TEST(CheckOutFrame(frameOut, packet.sizeBytes, false) == 0);

    jb.ReleaseFrame(frameOut);

    jb.Flush();

    // Test that a we cannot get incomplete frames from the JB if we haven't
    // received the marker bit, unless we have received a packet from a later
    // timestamp.

    packet.seqNum += 2;
    packet.frameType = kVideoFrameDelta;
    packet.isFirstPacket = false;
    packet.markerBit = false;

    TEST(frameIn = jb.GetFrame(packet));
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    frameOut = jb.GetFrameForDecoding();
    TEST(frameOut == NULL);

    packet.seqNum += 2;
    packet.timestamp += 33 * 90;

    TEST(frameIn = jb.GetFrame(packet));
    TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));

    frameOut = jb.GetFrameForDecoding();

    TEST(frameOut != NULL);
    TEST(CheckOutFrame(frameOut, packet.sizeBytes, false) == 0);
    jb.ReleaseFrame(frameOut);

    jb.Stop();

    printf("DONE !!!\n");

    printf("\nVCM Jitter Buffer Test: \n\n%i tests completed\n",
           vcmMacrosTests);
    if (vcmMacrosErrors > 0)
    {
        printf("%i FAILED\n\n", vcmMacrosErrors);
    }
    else
    {
        printf("ALL PASSED\n\n");
    }

    return 0;

}
