static void
dissect_mime_encap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item* item;
    guint len;

    /* XXX, COL_INFO */

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "MIME_FILE");
    item = proto_tree_add_item(tree, proto_mime_encap, tvb, 0, -1, ENC_NA);

    /* frames with nsec >= 1000000000 means errors :) */
    if (pinfo->fd->abs_ts.nsecs >= 1000000000) {
        proto_item_append_text(item, " (Error)");
        /* return; */ /* dissect what we have */
    }

    len = tvb_length(tvb);
    if (!pinfo->fd->flags.visited) {
        if (len) {
            tvbuff_t *cloned_tvb = tvb_clone(tvb);

            if (!file_tvbs) {
                file_tvbs = cloned_tvb;
                whole_tvb = tvb_new_composite();
            } else
                tvb_add_to_chain(file_tvbs, cloned_tvb);

            tvb_composite_append(whole_tvb, cloned_tvb);
        } else
            tvb_composite_finalize(whole_tvb);
    }

    /* End of file? */
    if (!len && whole_tvb) {
        /*
         * Here we're doing some trick.
         *
         * We don't want to call dissectors with composite tvb, cause dissectors can create subsets or real data child
         * on it, which would append to whole_tvb chain and would be freed only in mime_encap_init.
         *
         * So we create some tvb which pass all calls to whole_tvb, but chain with tvb (which is freed in dissection cleanup)
         */
        tvbuff_t *tmp_tvb = tvb_new_chain(tvb, whole_tvb);

        proto_item_append_text(item, " (Final)");

        add_new_data_source(pinfo, tmp_tvb, "Whole file");

        if (!dissector_try_heuristic(heur_subdissector_list, tmp_tvb, pinfo, tree, NULL)) {
            proto_item_append_text(item, " (Unhandled)");
            call_dissector(data_handle, tmp_tvb, pinfo, tree);
        }
    }
}
static void
dissect_dvb_data_mpe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{

    guint       offset = 0, tot_len = 0;
    guint8      llc_snap_flag;
    int         i;

    proto_item *ti;
    proto_tree *dvb_data_mpe_tree;
    tvbuff_t   *mac_tvb;
    tvbuff_t   *mac_bytes_tvb[6];
    tvbuff_t   *data_tvb;

    /* The TVB should start right after the section_length in the Section packet */

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "DVB-DATA");
    col_set_str(pinfo->cinfo, COL_INFO, "MultiProtocol Encapsulation");

    ti = proto_tree_add_item(tree, proto_dvb_data_mpe, tvb, offset, -1, ENC_NA);
    dvb_data_mpe_tree = proto_item_add_subtree(ti, ett_dvb_data_mpe);

    offset += packet_mpeg_sect_header(tvb, offset, dvb_data_mpe_tree, &tot_len, NULL);


    /* Parse the DMC-CC private section header */

    mac_bytes_tvb[5] = tvb_new_subset(tvb, offset, 1, 1);
    offset += 1;
    mac_bytes_tvb[4] = tvb_new_subset(tvb, offset, 1, 1);
    offset += 1;

    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_reserved,                   tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_payload_scrambling_control, tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_address_scrambling_control, tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_llc_snap_flag,              tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_current_next_indicator,     tvb, offset, 1, ENC_BIG_ENDIAN);
    llc_snap_flag = tvb_get_guint8(tvb, offset) & DVB_DATA_MPE_LLC_SNAP_FLAG_MASK;
    offset += 1;

    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_section_number,             tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;

    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_last_section_number,        tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;

    for (i = 3; i >= 0; i--) {
        mac_bytes_tvb[i] = tvb_new_subset(tvb, offset, 1, 1);
        offset += 1;
    }

    mac_tvb = tvb_new_composite();

    for (i = 0; i < 6; i++)
        tvb_composite_append(mac_tvb, mac_bytes_tvb[i]);

    tvb_composite_finalize(mac_tvb);

    proto_tree_add_item(dvb_data_mpe_tree, hf_dvb_data_mpe_dst_mac, mac_tvb, 0 , 6, ENC_NA);
    col_add_str(pinfo->cinfo, COL_RES_DL_DST, tvb_ether_to_str(mac_tvb, 0));

    data_tvb = tvb_new_subset_remaining(tvb, offset);

    if (llc_snap_flag) {
        call_dissector(llc_handle, data_tvb, pinfo, tree);
    } else {
        call_dissector(ip_handle, data_tvb, pinfo, tree);
    }

    packet_mpeg_sect_crc(tvb, pinfo, dvb_data_mpe_tree, 0, tot_len - 1);
    return;

}
static void
dissect_rtmpt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	proto_tree	*rtmpt_tree = NULL;
	proto_tree	*rtmptroot_tree = NULL;
	proto_item	*ti = NULL;
	gint        offset = 0;

	struct tcpinfo* tcpinfo = pinfo->private_data;

	guint8  iCommand = -1;
	guint32 iLength = 1;
	guint16 iHeaderType = 4;
	guint16 iHeaderLength;
	guint8  iID;
	guint   rtmp_index;

	conversation_t * current_conversation;
	rtmpt_conversation_data_t * conversation_data;
	rtmpt_packet_data_t * packet_data;

	rtmpt_chunk_data_t *current_chunk_data = NULL;
	rtmpt_chunk_data_t *initial_chunk_data = NULL;

	tvbuff_t*   amf_tvb;

	current_conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);

	if (NULL != current_conversation)
	{
		conversation_data = (rtmpt_conversation_data_t*)conversation_get_proto_data(current_conversation, proto_rtmpt);
		if (NULL == conversation_data)
		{
			conversation_data = se_alloc(sizeof(rtmpt_conversation_data_t));
			memset((void*)conversation_data, 0, sizeof(rtmpt_conversation_data_t));
			conversation_add_proto_data(current_conversation, proto_rtmpt, conversation_data);
			conversation_data->current_chunks = g_hash_table_new(g_direct_hash, g_direct_equal);
			conversation_data->previous_frame_number = -1;
			conversation_data->current_chunk_size = RTMPT_DEFAULT_CHUNK_SIZE;
			conversation_data->is_rtmpe = 0;
		}

		packet_data = p_get_proto_data(pinfo->fd, proto_rtmpt);
		if (NULL == packet_data)
		{
			packet_data = se_alloc(sizeof(rtmpt_packet_data_t));
			memset((void*)packet_data, 0, sizeof(rtmpt_packet_data_t));
			p_add_proto_data(pinfo->fd, proto_rtmpt, packet_data);
			packet_data->initial_chunks = g_hash_table_new(g_direct_hash, g_direct_equal);
			packet_data->initial_chunk_size = conversation_data->current_chunk_size;
		}


		if (conversation_data->is_rtmpe == 1)
		{
			col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMPE");
			return;
		}
		else
		{
			col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
		}

		if (conversation_data->previous_frame_number != (guint) pinfo->fd->num)
		{
			conversation_data->current_chunk_size = packet_data->initial_chunk_size;
		}

		col_set_writable(pinfo->cinfo, TRUE);
		col_clear(pinfo->cinfo, COL_INFO);

		conversation_data->previous_frame_number = pinfo->fd->num;
		if (tvb_length_remaining(tvb, offset) >= 1)
		{
			if (tcpinfo->lastackseq == RTMPT_HANDSHAKE_OFFSET_1 && tcpinfo->seq == RTMPT_HANDSHAKE_OFFSET_1)
			{
				iCommand =  RTMPT_TYPE_HANDSHAKE_1;
			}
			else if (tcpinfo->lastackseq == RTMPT_HANDSHAKE_OFFSET_2 && tcpinfo->seq == RTMPT_HANDSHAKE_OFFSET_1) iCommand =  RTMPT_TYPE_HANDSHAKE_2;
			else if (tcpinfo->seq == RTMPT_HANDSHAKE_OFFSET_2
			         && tvb_length(tvb) == RTMPT_HANDSHAKE_LENGTH_3) iCommand = RTMPT_TYPE_HANDSHAKE_3;
			else
			{
				iID = tvb_get_guint8(tvb, offset + 0);
				iHeaderType = iID >> 6;
				rtmp_index = iID & 0x3F;

				current_chunk_data = g_hash_table_lookup(conversation_data->current_chunks, GUINT_TO_POINTER(rtmp_index));
				initial_chunk_data = g_hash_table_lookup(packet_data->initial_chunks, GUINT_TO_POINTER(rtmp_index));

				if (iHeaderType <= 2) iLength = tvb_get_ntoh24(tvb, offset + 4);
				if (iHeaderType <= 1)
				{
					iCommand = tvb_get_guint8(tvb, offset + 7);
					if (NULL == current_chunk_data)
					{
						current_chunk_data = se_alloc(sizeof(rtmpt_chunk_data_t));
						memset((void*)current_chunk_data, 0, sizeof(rtmpt_chunk_data_t));
						g_hash_table_insert(conversation_data->current_chunks, GUINT_TO_POINTER(rtmp_index), current_chunk_data);
					}

					current_chunk_data->data_type = iCommand;
					current_chunk_data->last_length = iLength;
					current_chunk_data->frame_modified = pinfo->fd->num;
				}
				else
				{
					/* must get the command type from the previous entries in the hash table */
					/* try to use the current_chunk_data unless it is from a different frame */
					if (NULL != current_chunk_data && NULL != initial_chunk_data)
					{
						/* we have precedent data (we should)*/
						if (current_chunk_data->frame_modified != pinfo->fd->num)
						{
							iCommand = initial_chunk_data->data_type;
							iLength = initial_chunk_data->length_remaining;
							current_chunk_data->frame_modified = pinfo->fd->num;
							current_chunk_data->data_type = iCommand;
							current_chunk_data->last_length = iLength;
							current_chunk_data->dechunk_buffer = initial_chunk_data->dechunk_buffer;
						}
						else
						{
							iCommand = current_chunk_data->data_type;
							iLength = current_chunk_data->length_remaining;
						}

						if (iLength > conversation_data->current_chunk_size)
						{
							iLength = conversation_data->current_chunk_size;
						}
					}
				}
			}

			iHeaderLength = rtmpt_header_length_from_type(iHeaderType);


			if (check_col(pinfo->cinfo, COL_INFO))
			{
				col_append_sep_fstr(pinfo->cinfo, COL_INFO, " | ", "%s", val_to_str(iCommand, rtmpt_opcode_vals, "Unknown (0x%01x)"));
				col_set_fence(pinfo->cinfo, COL_INFO);
			}

			if (tree)
			{
				ti = proto_tree_add_item(tree, proto_rtmpt, tvb, offset, -1, FALSE);
				proto_item_append_text(ti, " (%s)", val_to_str(iCommand, rtmpt_opcode_vals, "Unknown (0x%01x)"));
				rtmptroot_tree = proto_item_add_subtree(ti, ett_rtmpt);

				ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, iHeaderLength, RTMPT_TEXT_RTMP_HEADER);
				proto_item_append_text(ti, " (%s)", val_to_str(iCommand, rtmpt_opcode_vals, "Unknown (0x%01x)"));
				rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_header);

				if (iHeaderType <= 3) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_objid, tvb, offset + 0, 1, FALSE);
				if (iHeaderType <= 2) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_timestamp, tvb, offset + 1, 3, FALSE);
				if (iHeaderType <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_body_size, tvb, offset + 4, 3, FALSE);
				if (iHeaderType <= 1) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_function, tvb, offset + 7, 1, FALSE);
				if (iHeaderType <= 0) proto_tree_add_item(rtmpt_tree, hf_rtmpt_header_source, tvb, offset + 8, 4, TRUE);

				if (iCommand == RTMPT_TYPE_HANDSHAKE_1)
				{
					proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_data, tvb, 1, 1536, FALSE);
				}
				else if (iCommand == RTMPT_TYPE_HANDSHAKE_2)
				{
					proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_data, tvb, 1, 1536, FALSE);
					proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_data, tvb, 1537, 1536, FALSE);
				}
				else if (iCommand == RTMPT_TYPE_HANDSHAKE_3)
				{
					proto_tree_add_item(rtmpt_tree, hf_rtmpt_handshake_data, tvb, 0, -1, FALSE);
				}
				else if (iCommand == RTMPT_TYPE_CHUNK_SIZE)
				{
					conversation_data->current_chunk_size = tvb_get_ntohl (tvb, offset + iHeaderLength);
				}

				offset = iHeaderLength;
				if (tvb_length_remaining(tvb, offset))
				{
					ti = proto_tree_add_text(rtmptroot_tree, tvb, offset, -1, RTMPT_TEXT_RTMP_BODY);
				}


				if (iCommand == RTMPT_TYPE_INVOKE || iCommand == RTMPT_TYPE_NOTIFY)
				{
					guint iChunkSize = tvb_length_remaining(tvb, iHeaderLength);
					/* we have data which will be AMF */
					/* we should add it to a new tvb */
					if (NULL != current_chunk_data)
					{
						if (NULL == current_chunk_data->dechunk_buffer)
						{
							/* we have to create a new tvbuffer */
							current_chunk_data->dechunk_buffer = tvb_new_composite();
						}
						if (!(current_chunk_data->dechunk_buffer->initialized))
						{
							/* add the existing data to the new buffer */
							tvb_composite_append(current_chunk_data->dechunk_buffer,
							                     tvb_new_real_data(tvb_memdup(tvb, iHeaderLength, iChunkSize), iChunkSize, iChunkSize));

							if (current_chunk_data->length_remaining <= 0)
							{
								guint amf_length;
								guint8* amf_data;

								tvb_composite_finalize(current_chunk_data->dechunk_buffer);

								amf_length = tvb_length(current_chunk_data->dechunk_buffer);

								if (amf_length == 0)
								{
									return;
								}


								amf_data = tvb_memdup(current_chunk_data->dechunk_buffer, 0, amf_length);

								amf_tvb = tvb_new_real_data(amf_data, tvb_length_remaining(current_chunk_data->dechunk_buffer, 0), tvb_length_remaining(current_chunk_data->dechunk_buffer, 0));

								add_new_data_source(pinfo, amf_tvb, "Dechunked AMF data");
								ti = proto_tree_add_item(tree, proto_rtmpt, amf_tvb, 0, -1, FALSE);
								rtmpt_tree = proto_item_add_subtree(ti, ett_rtmpt_body);
								proto_tree_set_appendix(rtmpt_tree, amf_tvb, 0, tvb_length_remaining(amf_tvb, 0));
								proto_item_append_text(rtmpt_tree, " (%s)", "AMF Data");
								dissect_rtmpt_amf(amf_tvb, rtmpt_tree);
								current_chunk_data->dechunk_buffer = NULL;
							}
						}
					}
				}
			}
		}
	}