//
// Copyright (c) 2013-2024 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_utest_protocol2.hpp>

using namespace std;

#include <srs_kernel_error.hpp>
#include <srs_core_autofree.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_protocol_rtmp_msg_array.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_st.hpp>
#include <srs_protocol_amf0.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_protocol_http_conn.hpp>
#include <srs_protocol_protobuf.hpp>
#include <srs_kernel_buffer.hpp>

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x20, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=1 header,
* last video changed length
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11Length)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x20, // length, 288
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x20, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x83,
                0x00, 0x00, 0x20, // timestamp
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=2 header,
* last video changed length
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12Length)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x20, // length, 288
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x83,
                0x00, 0x00, 0x20, // timestamp
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x110, msg->header.payload_length);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x120, msg->header.payload_length);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x120, msg->header.payload_length);
    }
}

/**
* recv video, with extended timestamp.
* small timestamp < 0xffffff
*/
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0xff, 0xff, 0xff, // timestamp
            0x00, 0x00, 0x04, // length
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            0x00, 0x00, 0x00, 0x10, // extended timestamp
            // msg payload start
            0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));

    SrsCommonMessage* msg_raw = NULL;
    HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
    SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

    EXPECT_TRUE(msg->header.is_video());
    EXPECT_EQ(0x10, msg->header.timestamp);
}

/**
* recv video, with extended timestamp.
* big timestamp > 0xffffff
*/
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage2)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0xff, 0xff, 0xff, // timestamp
            0x00, 0x00, 0x04, // length
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            0x7f, 0x01, 0x02, 0x03, // extended timestamp
            // msg payload start
            0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));

    SrsCommonMessage* msg_raw = NULL;
    HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
    SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

    EXPECT_TRUE(msg->header.is_video());
    EXPECT_EQ(0x7f010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp.
* always use 31bits timestamp.
*/
// always use 31bits timestamp, for some server may use 32bits extended timestamp.
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage3)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0xff, 0xff, 0xff, // timestamp
            0x00, 0x00, 0x04, // length
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            0xff, 0x01, 0x02, 0x03, // extended timestamp
            // msg payload start
            0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));

    SrsCommonMessage* msg_raw = NULL;
    HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
    SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

    EXPECT_TRUE(msg->header.is_video());
    // always use 31bits timestamp
    EXPECT_EQ(0x7f010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp, in 2 chunks packet.
* always send extended timestamp in 0xCX chunk packets.
*/
/**
* RTMP specification and ffmpeg/librtmp is false,
* but, adobe changed the specification, so flash/FMLE/FMS always true.
* default to true to support flash/FMLE/FMS.
*
* ffmpeg/librtmp may donot send this filed, need to detect the value.
* @see also: http://blog.csdn.net/win_lin/article/details/13363699
* compare to the chunk timestamp, which is set by chunk message header
* type 0,1 or 2.
*
* @remark, nginx send the extended-timestamp in sequence-header,
* and timestamp delta in continue C1 chunks, and so compatible with ffmpeg,
* that is, there is no continue chunks and extended-timestamp in nginx-rtmp.
*
* @remark, srs always send the extended-timestamp, to keep simple,
* and compatible with adobe products.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    // video message
    uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0xff, 0xff, 0xff, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            0x00, 0x01, 0x02, 0x03, // extended timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
            // chunk #2
            0xC3,
            0x00, 0x01, 0x02, 0x03, // extended timestamp
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            // chunk #2
            0xC3,
            0x00, 0x01, 0x02, 0x03, // extended timestamp
            /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));

    SrsCommonMessage* msg_raw = NULL;
    HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
    SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

    EXPECT_TRUE(msg->header.is_video());
    // 0xCX with extended timestamp.
    EXPECT_EQ(0x00010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp, in 2 chunks packet.
* never send extended timestamp in 0xCX chunk packets.
*/
// FFMPEG/librtmp, RTMP specification standard protocol.
VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk2)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    // video message
    uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0xff, 0xff, 0xff, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            0x00, 0x01, 0x02, 0x03, // extended timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
            // chunk #2
            0xC3,
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            // chunk #2
            0xC3,
            /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));

    SrsCommonMessage* msg_raw = NULL;
    HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
    SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

    EXPECT_TRUE(msg->header.is_video());
    // 0xCX without extended timestamp.
    EXPECT_EQ(0x00010203, msg->header.timestamp);
}

/**
* a video message, in 2 chunks packet.
* use 1B chunk header, min chunk id is 2.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BMin)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    // video message
    uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x02,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
            // chunk #2
            0xC2, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            // chunk #2
            0xC2, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));

    SrsCommonMessage* msg_raw = NULL;
    HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
    SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

    EXPECT_TRUE(msg->header.is_video());
    // 1B cid(6bits), min is 2
    EXPECT_EQ(0x02, msg->header.prefer_cid);
}

VOID TEST(ProtocolKbpsTest, Connections)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsUniquePtr<SrsKbps> kbps_uptr(kbps);

        SrsNetworkDelta* delta = new SrsNetworkDelta();
        SrsUniquePtr<SrsNetworkDelta> delta_uptr(delta);
        delta->set_io(io, io);

        // No data, 0kbps.
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000)->set_out(30 * 100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000)->set_out(330 * 100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }

    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsUniquePtr<SrsKbps> kbps_uptr(kbps);

        SrsNetworkDelta* delta = new SrsNetworkDelta();
        SrsUniquePtr<SrsNetworkDelta> delta_uptr(delta);
        delta->set_io(io, io);

        // No data, 0kbps.
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }

    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsUniquePtr<SrsKbps> kbps_uptr(kbps);

        SrsNetworkDelta* delta = new SrsNetworkDelta();
        SrsUniquePtr<SrsNetworkDelta> delta_uptr(delta);
        delta->set_io(io, io);

        // No data, 0kbps.
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(330 * 100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, Delta)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkDelta* delta = new SrsNetworkDelta();
        SrsUniquePtr<SrsNetworkDelta> delta_uptr(delta);
        delta->set_io(io, io);

        // No data.
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);

        // 800kb.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);

        // No data.
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
    }

    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkDelta* delta = new SrsNetworkDelta();
        SrsUniquePtr<SrsNetworkDelta> delta_uptr(delta);
        delta->set_io(io, io);

        // No data.
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);

        // 800kb.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);

        // Kbps without io, gather delta.
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsUniquePtr<SrsKbps> kbps_uptr(kbps);

        // No data, 0kbps.
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        kbps->add_delta(30 * in, 30 * out);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, RAWStatistic)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkDelta* delta = new SrsNetworkDelta();
        SrsUniquePtr<SrsNetworkDelta> delta_uptr(delta);
        delta->set_io(io, io);

        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsUniquePtr<SrsKbps> kbps_uptr(kbps);

        // No data, 0kbps.
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }

    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);

        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsUniquePtr<SrsKbps> kbps_uptr(kbps);

        // No io, no data.
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());

        // With io, zero data.
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkDelta* delta = new SrsNetworkDelta();
        SrsUniquePtr<SrsNetworkDelta> delta_uptr(delta);
        delta->set_io(io, io);

        kbps->add_delta(delta);
        kbps->sample();
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());

        // With io with data.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        kbps->add_delta(delta);
        kbps->sample();
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());

        // No io, cached data.
        delta->set_io(NULL, NULL);
        kbps->add_delta(delta);
        kbps->sample();
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());

        // Use the same IO, but as a fresh io.
        delta->set_io(io, io);
        kbps->add_delta(delta);
        kbps->sample();
        EXPECT_EQ(200 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(200 * 1000, kbps->get_send_bytes());

        io->set_in(150 * 1000)->set_out(150 * 1000);
        kbps->add_delta(delta);
        kbps->sample();
        EXPECT_EQ(250 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(250 * 1000, kbps->get_send_bytes());

        // No io, cached data.
        delta->set_io(NULL, NULL);
        kbps->add_delta(delta);
        kbps->sample();
        EXPECT_EQ(250 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(250 * 1000, kbps->get_send_bytes());
    }
}

VOID TEST(ProtocolKbpsTest, WriteLargeIOVs)
{
    srs_error_t err;

    if (true) {
        iovec iovs[1];
        iovs[0].iov_base = (char*)"Hello";
        iovs[0].iov_len = 5;

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, 1, &nn));
        EXPECT_EQ(5, nn);
        EXPECT_EQ(5, io.sbytes);
    }

    if (true) {
        iovec iovs[1024];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }

    if (true) {
        iovec iovs[1025];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }

    if (true) {
        iovec iovs[4096];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }
}

VOID TEST(ProtocolKbpsTest, ConnectionsSugar)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkKbps* kbps = new SrsNetworkKbps(clock->set_clock(0));
        SrsUniquePtr<SrsNetworkKbps> kbps_uptr(kbps);
        kbps->set_io(io, io);

        // No data, 0kbps.
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000)->set_out(30 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000)->set_out(330 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }

    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkKbps* kbps = new SrsNetworkKbps(clock->set_clock(0));
        SrsUniquePtr<SrsNetworkKbps> kbps_uptr(kbps);
        kbps->set_io(io, io);

        // No data, 0kbps.
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }

    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkKbps* kbps = new SrsNetworkKbps(clock->set_clock(0));
        SrsUniquePtr<SrsNetworkKbps> kbps_uptr(kbps);
        kbps->set_io(io, io);

        // No data, 0kbps.
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(330 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, DeltaSugar)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        // Kbps without io, gather delta.
        SrsNetworkKbps* kbps = new SrsNetworkKbps(clock->set_clock(0));
        SrsUniquePtr<SrsNetworkKbps> kbps_uptr(kbps);
        kbps->set_io(io, io);

        // No data, 0kbps.
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000)->set_out(30 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, RAWStatisticSugar)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);

        SrsNetworkKbps* kbps = new SrsNetworkKbps(clock->set_clock(0));
        SrsUniquePtr<SrsNetworkKbps> kbps_uptr(kbps);
        kbps->set_io(io, io);

        // No data, 0kbps.
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());

        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->sample();

        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());

        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }

    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsUniquePtr<MockWallClock> clock_uptr(clock);

        SrsNetworkKbps* kbps = new SrsNetworkKbps(clock->set_clock(0));
        SrsUniquePtr<SrsNetworkKbps> kbps_uptr(kbps);

        // No io, no data.
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());

        // With io, zero data.
        MockStatistic* io = new MockStatistic();
        SrsUniquePtr<MockStatistic> io_uptr(io);
        kbps->set_io(io, io);

        kbps->sample();
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());

        // With io with data.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        kbps->sample();
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());

        // No io, cached data.
        kbps->set_io(NULL, NULL);
        kbps->sample();
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());

        // Use the same IO, but as a fresh io.
        kbps->set_io(io, io);
        kbps->sample();
        EXPECT_EQ(200 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(200 * 1000, kbps->get_send_bytes());

        io->set_in(150 * 1000)->set_out(150 * 1000);
        kbps->sample();
        EXPECT_EQ(250 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(250 * 1000, kbps->get_send_bytes());

        // No io, cached data.
        kbps->set_io(NULL, NULL);
        kbps->sample();
        EXPECT_EQ(250 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(250 * 1000, kbps->get_send_bytes());
    }
}

VOID TEST(ProtocolKbpsTest, StreamIdentify)
{
    EXPECT_STREQ("/live/livestream", srs_generate_stream_url("", "live", "livestream").c_str());
    EXPECT_STREQ("/live/livestream", srs_generate_stream_url("", "live", "livestream.flv").c_str());
    EXPECT_STREQ("/live/livestream", srs_generate_stream_url("", "live", "livestream.m3u8").c_str());
    EXPECT_STREQ("/live/livestream", srs_generate_stream_url("__defaultVhost__", "live", "livestream").c_str());

    EXPECT_STREQ("ossrs.io/live/livestream", srs_generate_stream_url("ossrs.io", "live", "livestream").c_str());
    EXPECT_STREQ("ossrs.io/live/livestream", srs_generate_stream_url("ossrs.io", "live", "livestream.flv").c_str());
    EXPECT_STREQ("ossrs.io/live/livestream", srs_generate_stream_url("ossrs.io", "live", "livestream.m3u8").c_str());
}

VOID TEST(ProtocolHTTPTest, ParseHTTPMessage)
{
    srs_error_t err = srs_success;

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));

        // We should read body, or next parsing message will fail.
        // @see https://github.com/ossrs/srs/issues/1181
        EXPECT_FALSE(req->body_reader()->eof());
        srs_freep(req);

        // Got new packet, notice that previous body still exists in bio.
        bio.append("GET /gslb/v2/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");

        // Should fail because there is body which not read.
        // @see https://github.com/ossrs/srs/issues/1181
        HELPER_ASSERT_FAILED(hp.parse_message(&bio, &req));
        srs_freep(req);
    }

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

        // Should fail if not completed message.
        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_FAILED(hp.parse_message(&bio, &req));
        srs_freep(req);
    }

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
        SrsUniquePtr<ISrsHttpMessage> req_uptr(req);

        char v[64] = {0};
        HELPER_ASSERT_SUCCESS(req->body_reader()->read(v, sizeof(v), NULL));
        EXPECT_TRUE(string("Hello") == string(v));

        EXPECT_TRUE(req->body_reader()->eof());
    }

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 0\r\n\r\n");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
        SrsUniquePtr<ISrsHttpMessage> req_uptr(req);
    }

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
        SrsUniquePtr<ISrsHttpMessage> req_uptr(req);
    }

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
        SrsUniquePtr<ISrsHttpMessage> req_uptr(req);
    }
}

VOID TEST(ProtocolProtobufTest, VarintsSize)
{
    EXPECT_EQ(1,  SrsProtobufVarints::sizeof_varint(               0x00));
    EXPECT_EQ(1,  SrsProtobufVarints::sizeof_varint(               0x70));
    EXPECT_EQ(1,  SrsProtobufVarints::sizeof_varint(               0x7f));
    EXPECT_EQ(2,  SrsProtobufVarints::sizeof_varint(               0x80));
    EXPECT_EQ(2,  SrsProtobufVarints::sizeof_varint(             0x3ff0));
    EXPECT_EQ(2,  SrsProtobufVarints::sizeof_varint(             0x3fff));
    EXPECT_EQ(3,  SrsProtobufVarints::sizeof_varint(             0x4000));
    EXPECT_EQ(3,  SrsProtobufVarints::sizeof_varint(           0x1ffff0));
    EXPECT_EQ(3,  SrsProtobufVarints::sizeof_varint(           0x1fffff));
    EXPECT_EQ(4,  SrsProtobufVarints::sizeof_varint(           0x200000));
    EXPECT_EQ(4,  SrsProtobufVarints::sizeof_varint(         0x0ffffff0));
    EXPECT_EQ(4,  SrsProtobufVarints::sizeof_varint(         0x0fffffff));
    EXPECT_EQ(5,  SrsProtobufVarints::sizeof_varint(         0x10000000));
    EXPECT_EQ(5,  SrsProtobufVarints::sizeof_varint(        0x7fffffff0));
    EXPECT_EQ(5,  SrsProtobufVarints::sizeof_varint(        0x7ffffffff));
    EXPECT_EQ(6,  SrsProtobufVarints::sizeof_varint(        0x800000000));
    EXPECT_EQ(6,  SrsProtobufVarints::sizeof_varint(      0x3fffffffff0));
    EXPECT_EQ(6,  SrsProtobufVarints::sizeof_varint(      0x3ffffffffff));
    EXPECT_EQ(7,  SrsProtobufVarints::sizeof_varint(      0x40000000000));
    EXPECT_EQ(7,  SrsProtobufVarints::sizeof_varint(    0x1fffffffffff0));
    EXPECT_EQ(7,  SrsProtobufVarints::sizeof_varint(    0x1ffffffffffff));
    EXPECT_EQ(8,  SrsProtobufVarints::sizeof_varint(    0x2000000000000));
    EXPECT_EQ(8,  SrsProtobufVarints::sizeof_varint(  0x0fffffffffffff0));
    EXPECT_EQ(8,  SrsProtobufVarints::sizeof_varint(  0x0ffffffffffffff));
    EXPECT_EQ(9,  SrsProtobufVarints::sizeof_varint(  0x100000000000000));
    EXPECT_EQ(9,  SrsProtobufVarints::sizeof_varint( 0x7ffffffffffffff0));
    EXPECT_EQ(9,  SrsProtobufVarints::sizeof_varint( 0x7fffffffffffffff));
    EXPECT_EQ(10, SrsProtobufVarints::sizeof_varint(0x8000000000000000));
    EXPECT_EQ(10, SrsProtobufVarints::sizeof_varint(0xfffffffffffffff0));
    EXPECT_EQ(10, SrsProtobufVarints::sizeof_varint(0xffffffffffffffff));
}

VOID TEST(ProtocolProtobufTest, VarintsEncode)
{
    srs_error_t  err = srs_success;
    static char buf[128];

    if (true) {
        SrsBuffer b(buf, 1); uint8_t expect[] = {0x00};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 1); uint8_t expect[] = {0x70};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x70)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 1); uint8_t expect[] = {0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x7f)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 2); uint8_t expect[] = {0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x80)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 2); uint8_t expect[] = {0xf0, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x3ff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 2); uint8_t expect[] = {0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x3fff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 3); uint8_t expect[] = {0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x4000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 3); uint8_t expect[] = {0xf0, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x1ffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 3); uint8_t expect[] = {0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x1fffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 4); uint8_t expect[] = {0x80, 0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x200000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 4); uint8_t expect[] = {0xf0, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0xffffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 4); uint8_t expect[] = {0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0xfffffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 5); uint8_t expect[] = {0x80, 0x80, 0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x10000000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 5); uint8_t expect[] = {0xf0, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x7fffffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 5); uint8_t expect[] = {0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x7ffffffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 6); uint8_t expect[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x800000000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 6); uint8_t expect[] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x3fffffffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 6); uint8_t expect[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x3ffffffffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 7); uint8_t expect[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x40000000000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 7); uint8_t expect[] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x1fffffffffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 7); uint8_t expect[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x1ffffffffffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 8); uint8_t expect[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x2000000000000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 8); uint8_t expect[] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0xfffffffffffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 8); uint8_t expect[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0xffffffffffffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 9); uint8_t expect[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x100000000000000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 9); uint8_t expect[] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x7ffffffffffffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 9); uint8_t expect[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x7fffffffffffffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 10); uint8_t expect[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0x8000000000000000)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 10); uint8_t expect[] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0xfffffffffffffff0)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
    if (true) {
        SrsBuffer b(buf, 10); uint8_t expect[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01};
        HELPER_ASSERT_SUCCESS(SrsProtobufVarints::encode(&b, 0xffffffffffffffff)); EXPECT_TRUE(srs_bytes_equals(buf, (char*)expect, sizeof(expect)));
    }
}

VOID TEST(ProtocolProtobufTest, String)
{
    srs_error_t  err = srs_success;
    static char buf[128];

    if (true) {
        EXPECT_EQ(1 + 10, SrsProtobufString::sizeof_string("HelloWorld"));

        SrsBuffer b(buf, 1 + 10);
        HELPER_ASSERT_SUCCESS(SrsProtobufString::encode(&b, "HelloWorld"));

        uint8_t expect[] = {0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64};
        EXPECT_TRUE(srs_bytes_equals(buf, (char *) expect, sizeof(expect)));
    }

    if (true) {
        EXPECT_EQ(1, SrsProtobufString::sizeof_string(""));

        SrsBuffer b(buf, 1);
        HELPER_ASSERT_SUCCESS(SrsProtobufString::encode(&b, ""));

        uint8_t expect[] = {0x00};
        EXPECT_TRUE(srs_bytes_equals(buf, (char *) expect, sizeof(expect)));
    }
}

class MockProtobufObject : public ISrsEncoder
{
public:
    uint64_t nb_bytes() {
        return 1;
    }
    srs_error_t encode(SrsBuffer* b) {
        b->write_1bytes(0x0f);
        return srs_success;
    }
};

VOID TEST(ProtocolProtobufTest, FieldKey)
{
    srs_error_t  err = srs_success;
    static char buf[128];

    EXPECT_EQ(2, SrsProtobufFieldString);
    EXPECT_EQ(2, SrsProtobufFieldBytes);
    EXPECT_EQ(2, SrsProtobufFieldObject);
    EXPECT_EQ(2, SrsProtobufFieldLengthDelimited);

    EXPECT_EQ(1, SrsProtobufKey::sizeof_key());

    MockProtobufObject obj;
    EXPECT_EQ(2, SrsProtobufObject::sizeof_object(&obj));
    if (true) {
        SrsBuffer b(buf, 2);
        HELPER_ASSERT_SUCCESS(SrsProtobufObject::encode(&b, &obj));
        EXPECT_EQ(0x01, buf[0]); EXPECT_EQ(0x0f, buf[1]);
    }

    // Encode the field key as [ID=1, TYPE=2(Length delimited)]
    if (true) {
        SrsBuffer b(buf, 1);
        HELPER_ASSERT_SUCCESS(SrsProtobufKey::encode(&b, 1, SrsProtobufFieldLengthDelimited));
        EXPECT_EQ(0x0a, buf[0]);
    }

    // Encode the field value as [ID=2, TYPE=2(Length delimited)]
    if (true) {
        SrsBuffer b(buf, 1);
        HELPER_ASSERT_SUCCESS(SrsProtobufKey::encode(&b, 2, SrsProtobufFieldLengthDelimited));
        EXPECT_EQ(0x12, buf[0]);
    }

    // Encode the field time as [ID=1, TYPE=0(Varint)]
    if (true) {
        SrsBuffer b(buf, 1);
        HELPER_ASSERT_SUCCESS(SrsProtobufKey::encode(&b, 1, SrsProtobufFieldVarint));
        EXPECT_EQ(0x08, buf[0]);
    }

    // Encode the field source as [ID=4, TYPE=2(Length delimited)]
    if (true) {
        SrsBuffer b(buf, 1);
        HELPER_ASSERT_SUCCESS(SrsProtobufKey::encode(&b, 4, SrsProtobufFieldLengthDelimited));
        EXPECT_EQ(0x22, buf[0]);
    }
}

VOID TEST(ProtocolKbpsTest, NewDelta)
{
    if (true) {
        SrsEphemeralDelta ed;

        ISrsKbpsDelta* delta = (ISrsKbpsDelta*)&ed;
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);

        ed.add_delta(100 * 1000, 100 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);

        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
    }

    if (true) {
        SrsNetworkDelta nd;

        ISrsKbpsDelta* delta = (ISrsKbpsDelta*)&nd;
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);

        MockStatistic ms;
        ms.set_in(100 * 1000)->set_out(100*1000);
        nd.set_io(&ms, &ms);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);

        ms.add_in(10 * 1000)->add_out(10 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(10 * 1000, in);
        EXPECT_EQ(10 * 1000, out);

        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVMessage)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x20, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x30, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt1)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x43,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt2)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x83,
                0x00, 0x00, 0x10, // timestamp
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x83,
                0x00, 0x00, 0x10, // timestamp
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=3 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt3)
{
    srs_error_t err = srs_success;

    MockBufferIO bio;
    SrsProtocol proto(&bio);

    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */

    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x03,
                0x00, 0x00, 0x10, // timestamp
                0x00, 0x01, 0x10, // length, 272
                0x09, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0x04,
                0x00, 0x00, 0x15, // timestamp
                0x00, 0x00, 0x90, // length, 144
                0x08, // message_type
                0x01, 0x00, 0x00, 0x00, // stream_id
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0xC3,
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC4, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
                // 12bytes header, 1byts chunk header, 11bytes msg heder
                0xC3,
                // msg payload start
                0x02, 0x00, 0x07, 0x63,
                0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
                0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
                0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
                0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
                0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
                0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
                0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
                0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
                0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
                0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
                0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
                0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
                0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
                0xC3, /*next chunk.*/
                0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }

    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg_raw = NULL;
        HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw));
        SrsUniquePtr<SrsCommonMessage> msg(msg_raw);

        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

struct MockStage
{
    http_parser parser;
    const char* at;
    size_t length;

    MockStage(http_parser* from);
};

MockStage::MockStage(http_parser* from)
{
    parser = *from;
    at = NULL;
    length = 0;
}

class MockParser
{
private:
    http_parser_settings settings;
    http_parser* parser;
    size_t parsed;
public:
    MockStage* message_begin;
    MockStage* url;
    MockStage* status;
    MockStage* header_field;
    MockStage* header_value;
    MockStage* headers_complete;
    MockStage* body;
    MockStage* message_complete;
    MockStage* chunk_header;
    MockStage* chunk_complete;
public:
    MockParser();
    virtual ~MockParser();
public:
    srs_error_t parse(string data);
private:
    static int on_message_begin(http_parser* parser);
    static int on_url(http_parser* parser, const char* at, size_t length);
    static int on_status(http_parser* parser, const char* at, size_t length);
    static int on_header_field(http_parser* parser, const char* at, size_t length);
    static int on_header_value(http_parser* parser, const char* at, size_t length);
    static int on_headers_complete(http_parser* parser);
    static int on_body(http_parser* parser, const char* at, size_t length);
    static int on_message_complete(http_parser* parser);
    static int on_chunk_header(http_parser* parser);
    static int on_chunk_complete(http_parser* parser);
};

MockParser::MockParser()
{
    parser = new http_parser();
    http_parser_init(parser, HTTP_REQUEST);
    parser->data = (void*)this;
    parsed = 0;

    memset(&settings, 0, sizeof(settings));
    settings.on_message_begin = on_message_begin;
    settings.on_url = on_url;
    settings.on_status = on_status;
    settings.on_header_field = on_header_field;
    settings.on_header_value = on_header_value;
    settings.on_headers_complete = on_headers_complete;
    settings.on_body = on_body;
    settings.on_message_complete = on_message_complete;
    settings.on_chunk_header = on_chunk_header;
    settings.on_chunk_complete = on_chunk_complete;

    message_begin = NULL;
    url = NULL;
    status = NULL;
    header_field = NULL;
    header_value = NULL;
    headers_complete = NULL;
    body = NULL;
    message_complete = NULL;
    chunk_header = NULL;
    chunk_complete = NULL;
}

MockParser::~MockParser()
{
    srs_freep(parser);

    srs_freep(message_begin);
    srs_freep(url);
    srs_freep(status);
    srs_freep(header_field);
    srs_freep(header_value);
    srs_freep(headers_complete);
    srs_freep(body);
    srs_freep(message_complete);
    srs_freep(chunk_header);
    srs_freep(chunk_complete);
}

int MockParser::on_message_begin(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->message_begin);
    obj->message_begin = new MockStage(parser);

    return 0;
}

int MockParser::on_url(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->url);
    obj->url = new MockStage(parser);
    obj->url->at = at;
    obj->url->length = length;

    return 0;
}

int MockParser::on_status(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->status);
    obj->status = new MockStage(parser);
    obj->status->at = at;
    obj->status->length = length;

    return 0;
}

int MockParser::on_header_field(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->header_field);
    obj->header_field = new MockStage(parser);
    obj->header_field->at = at;
    obj->header_field->length = length;

    return 0;
}

int MockParser::on_header_value(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->header_value);
    obj->header_value = new MockStage(parser);
    obj->header_value->at = at;
    obj->header_value->length = length;

    return 0;
}

int MockParser::on_headers_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->headers_complete);
    obj->headers_complete = new MockStage(parser);

    return 0;
}

int MockParser::on_body(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->body);
    obj->body = new MockStage(parser);
    obj->body->at = at;
    obj->body->length = length;

    return 0;
}

int MockParser::on_message_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->message_complete);
    obj->message_complete = new MockStage(parser);

    return 0;
}

int MockParser::on_chunk_header(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->chunk_header);
    obj->chunk_header = new MockStage(parser);

    return 0;
}

int MockParser::on_chunk_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

    srs_freep(obj->chunk_complete);
    obj->chunk_complete = new MockStage(parser);

    return 0;
}

srs_error_t MockParser::parse(string data)
{
    srs_error_t err = srs_success;

    const char* buf = (const char*)data.data();
    size_t size = (size_t)data.length();
    size_t nparsed = http_parser_execute(parser, &settings, buf, size);
    parsed = nparsed;

    if (nparsed != size) {
        return srs_error_new(-1, "nparsed=%d, size=%d", nparsed, size);
    }

    return err;
}

VOID TEST(ProtocolHTTPTest, HTTPParser)
{
    srs_error_t err;

    if (true) {
        MockParser parser;
        // size = 70, nparsed = 70, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
        EXPECT_EQ(70, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
        EXPECT_TRUE(!parser.body);
        EXPECT_TRUE(parser.headers_complete);
        EXPECT_TRUE(!parser.message_complete);
    }

    if (true) {
        MockParser parser;
        // size = 75, nparsed = 75, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
        EXPECT_EQ(75, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
        EXPECT_TRUE(parser.body && 5 == parser.body->length);
        EXPECT_TRUE(parser.headers_complete);
        EXPECT_TRUE(parser.message_complete);
    }

    if (true) {
        MockParser parser;
        // size = 150, nparsed = 150, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
        EXPECT_EQ(150, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
        EXPECT_EQ(70, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
        EXPECT_EQ(5, (int)parser.parser->content_length);

        // size = 79, nparsed = 5, nread = 1, content_length = -1, Header("Content-Length", 5)
        HELPER_EXPECT_FAILED(parser.parse("elloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
        EXPECT_EQ(5, (int)parser.parsed);
        EXPECT_EQ(1, (int)parser.parser->nread);
        EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
    }

    if (true) {
        MockParser parser;
        // size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
        EXPECT_EQ(70, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
        EXPECT_EQ(5, (int)parser.parser->content_length);

        // size = 80, nparsed = 70, nread = 0, content_length = 0, Header("Content-Length", 5)
        HELPER_EXPECT_SUCCESS(parser.parse("HelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
        EXPECT_EQ(80, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
        EXPECT_EQ(0, (int)parser.parser->content_length);
    }

    if (true) {
        MockParser parser;
        // size = 73, nparsed = 73, nread = 0, content_length = 2, Header("Content-Length", 5)
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHel"));
        EXPECT_EQ(73, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
        EXPECT_EQ(2, (int)parser.parser->content_length);
    }

    if (true) {
        MockParser parser;
        // size = 82, nparsed = 75, nread = 1, content_length = -1, Header("Content-Length", 5)
        HELPER_EXPECT_FAILED(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello World!"));
        EXPECT_EQ(75, (int)parser.parsed);
        EXPECT_EQ(1, (int)parser.parser->nread);
        EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
    }

    if (true) {
        MockParser parser;
        // size = 34, nparsed = 34, nread = 34
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHo"));
        EXPECT_EQ(34, (int)parser.parsed);
        EXPECT_EQ(34, (int)parser.parser->nread);

        // size = 41, nparsed = 41, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("st: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
        EXPECT_EQ(41, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 41, nparsed = 41, nread = 41
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: oss"));
        EXPECT_EQ(41, (int)parser.parsed);
        EXPECT_EQ(41, (int)parser.parser->nread);

        // size = 34, nparsed = 34, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("rs.net\r\nContent-Length: 5\r\n\r\nHello"));
        EXPECT_EQ(34, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 48, nparsed = 48, nread = 48
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r"));
        EXPECT_EQ(48, (int)parser.parsed);
        EXPECT_EQ(48, (int)parser.parser->nread);

        // size = 27, nparsed = 27, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("\nContent-Length: 5\r\n\r\nHello"));
        EXPECT_EQ(27, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 68, nparsed = 68, nread = 68
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n"));
        EXPECT_EQ(68, (int)parser.parsed);
        EXPECT_EQ(68, (int)parser.parser->nread);

        // size = 7, nparsed = 7, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("\r\nHello"));
        EXPECT_EQ(7, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 69, nparsed = 69, nread = 69
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r"));
        EXPECT_EQ(69, (int)parser.parsed);
        EXPECT_EQ(69, (int)parser.parser->nread);

        // size = 6, nparsed = 6, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("\nHello"));
        EXPECT_EQ(6, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 75, nparsed = 75, nread = 0
        HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
        EXPECT_EQ(75, (int)parser.parsed);
        EXPECT_EQ(0, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // nparsed = 2, size = 2, nread = 2
        HELPER_EXPECT_SUCCESS(parser.parse("GE"));
        EXPECT_EQ(2, (int)parser.parsed);
        EXPECT_EQ(2, (int)parser.parser->nread);

        // size = 0, nparsed = 1, nread=2
        HELPER_EXPECT_FAILED(parser.parse(""));
        EXPECT_EQ(1, (int)parser.parsed);
        EXPECT_EQ(2, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 2, nparsed = 2, nread = 2
        HELPER_EXPECT_SUCCESS(parser.parse("GE"));
        EXPECT_EQ(2, (int)parser.parsed);
        EXPECT_EQ(2, (int)parser.parser->nread);

        // size = 1, nparsed = 0, nread = 3
        HELPER_EXPECT_FAILED(parser.parse("X"));
        EXPECT_EQ(0, (int)parser.parsed);
        EXPECT_EQ(3, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 2, nparsed = 2, nread = 2
        HELPER_EXPECT_SUCCESS(parser.parse("GE"));
        EXPECT_EQ(2, (int)parser.parsed);
        EXPECT_EQ(2, (int)parser.parser->nread);

        // size = 1, nparsed = 1, nread = 3
        HELPER_EXPECT_SUCCESS(parser.parse("T"));
        EXPECT_EQ(1, (int)parser.parsed);
        EXPECT_EQ(3, (int)parser.parser->nread);
    }

    if (true) {
        MockParser parser;
        // size = 3, nparsed = 3, nread = 3
        HELPER_EXPECT_SUCCESS(parser.parse("GET"));
        EXPECT_EQ(3, (int)parser.parsed);
        EXPECT_EQ(3, (int)parser.parser->nread);
    }
}

VOID TEST(ProtocolKbpsTest, ParseUrlFailed)
{
    if (true) {
        string tcUrl = "rtmp://__defaultVhost__/live", stream = "livestream";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("rtmp", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(1935, port);
    }

    if (true) {
        string tcUrl = "rtmp://__defaultVhost__:1936/live", stream = "livestream";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("rtmp", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(1936, port);
    }

    if (true) {
        string tcUrl = "http://__defaultVhost__/live", stream = "livestream.flv";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("http", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(80, port);
    }

    if (true) {
        string tcUrl = "http://__defaultVhost__/live", stream = "livestream.m3u8";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("http", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(80, port);
    }

    if (true) {
        string tcUrl = "http://__defaultVhost__:8080/live", stream = "livestream.m3u8";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("http", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(8080, port);
    }

    if (true) {
        string tcUrl = "https://__defaultVhost__/live", stream = "livestream.flv";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("https", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(443, port);
    }

    if (true) {
        string tcUrl = "https://__defaultVhost__/live", stream = "livestream.m3u8";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("https", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(443, port);
    }

    if (true) {
        string tcUrl = "https://__defaultVhost__:8088/live", stream = "livestream.m3u8";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("https", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(8088, port);
    }

    if (true) {
        string tcUrl = "webrtc://__defaultVhost__/live", stream = "livestream";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("webrtc", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(80, port);
    }

    if (true) {
        string tcUrl = "webrtc://__defaultVhost__:8080/live", stream = "livestream";
        string schema, host, vhost, app, param;
        int port = 0;
        srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param);
        EXPECT_STREQ("webrtc", schema.c_str());
        EXPECT_STREQ("__defaultVhost__", host.c_str());
        EXPECT_STREQ("__defaultVhost__", vhost.c_str());
        EXPECT_EQ(8080, port);
    }
}

