/* packet-btavdtp.c
 * Routines for Bluetooth AVDTP dissection
 *
 * Copyright 2012, Michal Labedzki for Tieto Corporation
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>

#include "packet-bluetooth.h"
#include "packet-btl2cap.h"
#include "packet-btsdp.h"
#include "packet-btavdtp.h"
#include "packet-btavrcp.h"
#include "packet-rtp.h"

#define AVDTP_MESSAGE_TYPE_MASK  0x03
#define AVDTP_PACKET_TYPE_MASK   0x0C
#define AVDTP_TRANSACTION_MASK   0xF0
#define AVDTP_SIGNAL_ID_MASK     0x3F
#define AVDTP_RFA0_MASK          0xC0

#define MESSAGE_TYPE_COMMAND          0x00
#define MESSAGE_TYPE_GENERAL_REJECT   0x01
#define MESSAGE_TYPE_ACCEPT           0x02
#define MESSAGE_TYPE_REJECT           0x03

#define PACKET_TYPE_SINGLE            0x00
#define PACKET_TYPE_START             0x01
#define PACKET_TYPE_CONTINUE          0x02
#define PACKET_TYPE_END               0x03

#define SIGNAL_ID_DISCOVER                  0x01
#define SIGNAL_ID_GET_CAPABILITIES          0x02
#define SIGNAL_ID_SET_CONFIGURATION         0x03
#define SIGNAL_ID_GET_CONFIGURATION         0x04
#define SIGNAL_ID_RECONFIGURE               0x05
#define SIGNAL_ID_OPEN                      0x06
#define SIGNAL_ID_START                     0x07
#define SIGNAL_ID_CLOSE                     0x08
#define SIGNAL_ID_SUSPEND                   0x09
#define SIGNAL_ID_ABORT                     0x0A
#define SIGNAL_ID_SECURITY_CONTROL          0x0B
#define SIGNAL_ID_GET_ALL_CAPABILITIES      0x0C
#define SIGNAL_ID_DELAY_REPORT              0x0D

#define SERVICE_CATEGORY_MEDIA_TRANSPORT     0x01
#define SERVICE_CATEGORY_REPORTING           0x02
#define SERVICE_CATEGORY_RECOVERY            0x03
#define SERVICE_CATEGORY_CONTENT_PROTECTION  0x04
#define SERVICE_CATEGORY_HEADER_COMPRESSION  0x05
#define SERVICE_CATEGORY_MULTIPLEXING        0x06
#define SERVICE_CATEGORY_MEDIA_CODEC         0x07
#define SERVICE_CATEGORY_DELAY_REPORTING     0x08

#define MEDIA_TYPE_AUDIO   0x00
#define MEDIA_TYPE_VIDEO   0x01

#define SEID_ACP     0x00
#define SEID_INT     0x01

#define STREAM_TYPE_MEDIA   0x00
#define STREAM_TYPE_SIGNAL  0x01

#define CODEC_DEFAULT         0xFFFF
#define CODEC_SBC             0x00
#define CODEC_MPEG12_AUDIO    0x01
#define CODEC_MPEG24_AAC      0x02
#define CODEC_ATRAC           0x04
#define CODEC_APT_X           0xFF01
#define CODEC_APT_X_HD        0xFF24
#define CODEC_LDAC            0xFFAA

#define CODECID_APT_X         0x0001
#define CODECID_APT_X_HD      0x0024

#define CODEC_H263_BASELINE   0x01
#define CODEC_MPEG4_VSP       0x02
#define CODEC_H263_PROFILE_3  0x03
#define CODEC_H263_PROFILE_8  0x04

#define CODEC_VENDOR          0xFF

#define HEADER_SIZE  2
#define SEP_MAX     64
#define SEP_SIZE     2

/* ========================================================== */
/* Story: RTP Player, conversation (probably reassemble too) use address:port as
   "key" to separate devices/streams. In Bluetooth World it is not enough to
   separate devices/streams. Example key:
        guint32 interface_id (aka frame.interface_id)
        guint32 adapter_id (interface like "bluetooth-monitor" or USB provide
                            more than one device over interface, so we must
                            separate information provided by each one)
        guint16 hci_chandle (aka "connection handle" use to separate connections to devices)
        guint16 l2cap_psm (like hci_chandle but over l2cap layer, need hci_chandle info because
                           the same PSM can be used over chandles)
        guint8 rfcomm_channel (like l2cap_psm, but over RFCOMM layer...)
        etc. like
        guint8  stram_endpoint_number
        guint32 stream_number (to separate multiple streams for RTP Player)

    So keys can be various (length or type) and "ports" are not enough to sore
    all needed information. If one day that changed then all RTP_PLAYER_WORKAROUND
    block can be removed. This workaround use global number of streams (aka stream ID)
    to be used as port number in RTP Player to separate streams.
        */
#define RTP_PLAYER_WORKAROUND TRUE

#if RTP_PLAYER_WORKAROUND == TRUE
    wmem_tree_t *file_scope_stream_number = NULL;
#endif
/* ========================================================== */

static int proto_btavdtp = -1;

static int hf_btavdtp_data                                                 = -1;
static int hf_btavdtp_message_type                                         = -1;
static int hf_btavdtp_packet_type                                          = -1;
static int hf_btavdtp_transaction                                          = -1;
static int hf_btavdtp_signal                                               = -1;
static int hf_btavdtp_signal_id                                            = -1;
static int hf_btavdtp_rfa0                                                 = -1;
static int hf_btavdtp_number_of_signal_packets                             = -1;
static int hf_btavdtp_sep_seid                                             = -1;
static int hf_btavdtp_sep_inuse                                            = -1;
static int hf_btavdtp_sep_rfa0                                             = -1;
static int hf_btavdtp_sep_media_type                                       = -1;
static int hf_btavdtp_sep_type                                             = -1;
static int hf_btavdtp_sep_rfa1                                             = -1;
static int hf_btavdtp_error_code                                           = -1;
static int hf_btavdtp_acp_sep                                              = -1;
static int hf_btavdtp_acp_seid_item                                        = -1;
static int hf_btavdtp_int_seid_item                                        = -1;
static int hf_btavdtp_acp_seid                                             = -1;
static int hf_btavdtp_int_seid                                             = -1;
static int hf_btavdtp_service_category                                     = -1;
static int hf_btavdtp_rfa_seid                                             = -1;
static int hf_btavdtp_delay                                                = -1;
static int hf_btavdtp_length_of_service_category                           = -1;
static int hf_btavdtp_recovery_type                                        = -1;
static int hf_btavdtp_maximum_recovery_window_size                         = -1;
static int hf_btavdtp_maximum_number_of_media_packet_in_parity_code        = -1;
static int hf_btavdtp_multiplexing_fragmentation                           = -1;
static int hf_btavdtp_multiplexing_rfa                                     = -1;
static int hf_btavdtp_multiplexing_tsid                                    = -1;
static int hf_btavdtp_multiplexing_tcid                                    = -1;
static int hf_btavdtp_multiplexing_entry_rfa                               = -1;
static int hf_btavdtp_header_compression_backch                            = -1;
static int hf_btavdtp_header_compression_media                             = -1;
static int hf_btavdtp_header_compression_recovery                          = -1;
static int hf_btavdtp_header_compression_rfa                               = -1;
static int hf_btavdtp_content_protection_type                              = -1;
static int hf_btavdtp_media_codec_media_type                               = -1;
static int hf_btavdtp_media_codec_rfa                                      = -1;
static int hf_btavdtp_media_codec_unknown_type                             = -1;
static int hf_btavdtp_media_codec_audio_type                               = -1;
static int hf_btavdtp_media_codec_video_type                               = -1;
static int hf_btavdtp_sbc_sampling_frequency_16000                         = -1;
static int hf_btavdtp_sbc_sampling_frequency_32000                         = -1;
static int hf_btavdtp_sbc_sampling_frequency_44100                         = -1;
static int hf_btavdtp_sbc_sampling_frequency_48000                         = -1;
static int hf_btavdtp_sbc_channel_mode_mono                                = -1;
static int hf_btavdtp_sbc_channel_mode_dual_channel                        = -1;
static int hf_btavdtp_sbc_channel_mode_stereo                              = -1;
static int hf_btavdtp_sbc_channel_mode_joint_stereo                        = -1;
static int hf_btavdtp_sbc_block_4                                          = -1;
static int hf_btavdtp_sbc_block_8                                          = -1;
static int hf_btavdtp_sbc_block_12                                         = -1;
static int hf_btavdtp_sbc_block_16                                         = -1;
static int hf_btavdtp_sbc_subbands_4                                       = -1;
static int hf_btavdtp_sbc_subbands_8                                       = -1;
static int hf_btavdtp_sbc_allocation_method_snr                            = -1;
static int hf_btavdtp_sbc_allocation_method_loudness                       = -1;
static int hf_btavdtp_sbc_min_bitpool                                      = -1;
static int hf_btavdtp_sbc_max_bitpool                                      = -1;
static int hf_btavdtp_mpeg12_layer_1                                       = -1;
static int hf_btavdtp_mpeg12_layer_2                                       = -1;
static int hf_btavdtp_mpeg12_layer_3                                       = -1;
static int hf_btavdtp_mpeg12_crc_protection                                = -1;
static int hf_btavdtp_mpeg12_channel_mode_mono                             = -1;
static int hf_btavdtp_mpeg12_channel_mode_dual_channel                     = -1;
static int hf_btavdtp_mpeg12_channel_mode_stereo                           = -1;
static int hf_btavdtp_mpeg12_channel_mode_joint_stereo                     = -1;
static int hf_btavdtp_mpeg12_rfa                                           = -1;
static int hf_btavdtp_mpeg12_mpf_2                                         = -1;
static int hf_btavdtp_mpeg12_sampling_frequency_16000                      = -1;
static int hf_btavdtp_mpeg12_sampling_frequency_22050                      = -1;
static int hf_btavdtp_mpeg12_sampling_frequency_24000                      = -1;
static int hf_btavdtp_mpeg12_sampling_frequency_32000                      = -1;
static int hf_btavdtp_mpeg12_sampling_frequency_44100                      = -1;
static int hf_btavdtp_mpeg12_sampling_frequency_48000                      = -1;
static int hf_btavdtp_mpeg12_vbr_supported                                 = -1;
static int hf_btavdtp_mpeg12_bit_rate                                      = -1;
static int hf_btavdtp_mpeg24_object_type_mpeg2_aac_lc                      = -1;
static int hf_btavdtp_mpeg24_object_type_mpeg4_aac_lc                      = -1;
static int hf_btavdtp_mpeg24_object_type_mpeg4_aac_ltp                     = -1;
static int hf_btavdtp_mpeg24_object_type_mpeg4_aac_scalable                = -1;
static int hf_btavdtp_mpeg24_object_type_rfa                               = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_8000                       = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_11025                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_12000                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_16000                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_22050                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_24000                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_32000                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_44100                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_48000                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_64000                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_88200                      = -1;
static int hf_btavdtp_mpeg24_sampling_frequency_96000                      = -1;
static int hf_btavdtp_mpeg24_channels_1                                    = -1;
static int hf_btavdtp_mpeg24_channels_2                                    = -1;
static int hf_btavdtp_mpeg24_rfa                                           = -1;
static int hf_btavdtp_mpeg24_vbr_supported                                 = -1;
static int hf_btavdtp_mpeg24_bit_rate                                      = -1;
static int hf_btavdtp_atrac_version                                        = -1;
static int hf_btavdtp_atrac_channel_mode_single_channel                    = -1;
static int hf_btavdtp_atrac_channel_mode_dual_channel                      = -1;
static int hf_btavdtp_atrac_channel_mode_joint_stereo                      = -1;
static int hf_btavdtp_atrac_rfa1                                           = -1;
static int hf_btavdtp_atrac_rfa2                                           = -1;
static int hf_btavdtp_atrac_sampling_frequency_44100                       = -1;
static int hf_btavdtp_atrac_sampling_frequency_48000                       = -1;
static int hf_btavdtp_atrac_vbr_supported                                  = -1;
static int hf_btavdtp_atrac_bit_rate                                       = -1;
static int hf_btavdtp_atrac_maximum_sul                                    = -1;
static int hf_btavdtp_atrac_rfa3                                           = -1;
static int hf_btavdtp_vendor_specific_aptx_sampling_frequency_16000        = -1;
static int hf_btavdtp_vendor_specific_aptx_sampling_frequency_32000        = -1;
static int hf_btavdtp_vendor_specific_aptx_sampling_frequency_44100        = -1;
static int hf_btavdtp_vendor_specific_aptx_sampling_frequency_48000        = -1;
static int hf_btavdtp_vendor_specific_aptx_channel_mode_mono               = -1;
static int hf_btavdtp_vendor_specific_aptx_channel_mode_dual_channel       = -1;
static int hf_btavdtp_vendor_specific_aptx_channel_mode_stereo             = -1;
static int hf_btavdtp_vendor_specific_aptx_channel_mode_joint_stereo       = -1;
static int hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_16000      = -1;
static int hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_32000      = -1;
static int hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_44100      = -1;
static int hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_48000      = -1;
static int hf_btavdtp_vendor_specific_aptxhd_channel_mode_mono             = -1;
static int hf_btavdtp_vendor_specific_aptxhd_channel_mode_dual_channel     = -1;
static int hf_btavdtp_vendor_specific_aptxhd_channel_mode_stereo           = -1;
static int hf_btavdtp_vendor_specific_aptxhd_channel_mode_joint_stereo     = -1;
static int hf_btavdtp_vendor_specific_aptxhd_rfa                           = -1;
static int hf_btavdtp_vendor_specific_ldac_rfa1                            = -1;
static int hf_btavdtp_vendor_specific_ldac_sampling_frequency_44100        = -1;
static int hf_btavdtp_vendor_specific_ldac_sampling_frequency_48000        = -1;
static int hf_btavdtp_vendor_specific_ldac_sampling_frequency_88200        = -1;
static int hf_btavdtp_vendor_specific_ldac_sampling_frequency_96000        = -1;
static int hf_btavdtp_vendor_specific_ldac_sampling_frequency_176400       = -1;
static int hf_btavdtp_vendor_specific_ldac_sampling_frequency_192000       = -1;
static int hf_btavdtp_vendor_specific_ldac_rfa2                            = -1;
static int hf_btavdtp_vendor_specific_ldac_channel_mode_mono               = -1;
static int hf_btavdtp_vendor_specific_ldac_channel_mode_dual_channel       = -1;
static int hf_btavdtp_vendor_specific_ldac_channel_mode_stereo             = -1;
static int hf_btavdtp_h263_level_10                                        = -1;
static int hf_btavdtp_h263_level_20                                        = -1;
static int hf_btavdtp_h263_level_30                                        = -1;
static int hf_btavdtp_h263_level_rfa                                       = -1;
static int hf_btavdtp_mpeg4_level_0                                        = -1;
static int hf_btavdtp_mpeg4_level_1                                        = -1;
static int hf_btavdtp_mpeg4_level_2                                        = -1;
static int hf_btavdtp_mpeg4_level_3                                        = -1;
static int hf_btavdtp_mpeg4_level_rfa                                      = -1;
static int hf_btavdtp_vendor_id                                            = -1;
static int hf_btavdtp_vendor_specific_codec_id                             = -1;
static int hf_btavdtp_vendor_specific_value                                = -1;
static int hf_btavdtp_vendor_specific_apt_codec_id                         = -1;
static int hf_btavdtp_vendor_specific_ldac_codec_id                        = -1;
static int hf_btavdtp_capabilities                                         = -1;
static int hf_btavdtp_service                                              = -1;
static int hf_btavdtp_service_multiplexing_entry                           = -1;

static gint ett_btavdtp               = -1;
static gint ett_btavdtp_sep           = -1;
static gint ett_btavdtp_capabilities  = -1;
static gint ett_btavdtp_service       = -1;

static expert_field ei_btavdtp_sbc_min_bitpool_out_of_range = EI_INIT;
static expert_field ei_btavdtp_sbc_max_bitpool_out_of_range = EI_INIT;
static expert_field ei_btavdtp_unexpected_losc_data = EI_INIT;

static dissector_handle_t btavdtp_handle;
static dissector_handle_t bta2dp_handle;
static dissector_handle_t btvdp_handle;
static dissector_handle_t rtp_handle;

static wmem_tree_t *channels             = NULL;
static wmem_tree_t *sep_list             = NULL;
static wmem_tree_t *sep_open             = NULL;
static wmem_tree_t *media_packet_times   = NULL;

/* A2DP declarations */
static gint proto_bta2dp                        = -1;
static gint ett_bta2dp                          = -1;
static gint proto_bta2dp_cph_scms_t             = -1;
static gint ett_bta2dp_cph_scms_t               = -1;

static int hf_bta2dp_acp_seid                           = -1;
static int hf_bta2dp_int_seid                           = -1;
static int hf_bta2dp_codec                              = -1;
static int hf_bta2dp_vendor_id                          = -1;
static int hf_bta2dp_vendor_codec_id                    = -1;
static int hf_bta2dp_content_protection                 = -1;
static int hf_bta2dp_stream_start_in_frame              = -1;
static int hf_bta2dp_stream_end_in_frame                = -1;
static int hf_bta2dp_stream_number                      = -1;
static int hf_bta2dp_l_bit                              = -1;
static int hf_bta2dp_cp_bit                             = -1;
static int hf_bta2dp_reserved                           = -1;

static dissector_handle_t sbc_handle;
static dissector_handle_t mp2t_handle;
static dissector_handle_t mpeg_audio_handle;
static dissector_handle_t atrac_handle;

static gboolean  force_a2dp_scms_t = FALSE;
static gint      force_a2dp_codec = CODEC_DEFAULT;

static const enum_val_t pref_a2dp_codec[] = {
    { "default",     "Default",      CODEC_DEFAULT },
    { "sbc",         "SBC",          CODEC_SBC },
    { "mp2t",        "MPEG12 AUDIO", CODEC_MPEG12_AUDIO },
    { "mpeg-audio",  "MPEG24 AAC",   CODEC_MPEG24_AAC },
/* XXX: Not supported in Wireshark yet  { "atrac",      "ATRAC",                                  CODEC_ATRAC },*/
    { "aptx",        "aptX",         CODEC_APT_X },
    { "aptx-hd",     "aptX HD",      CODEC_APT_X_HD },
    { "ldac",        "LDAC",         CODEC_LDAC },
    { NULL, NULL, 0 }
};


/* VDP declarations */
static gint proto_btvdp                         = -1;
static gint ett_btvdp                           = -1;
static gint proto_btvdp_cph_scms_t              = -1;
static gint ett_btvdp_cph_scms_t                = -1;

static int hf_btvdp_acp_seid                           = -1;
static int hf_btvdp_int_seid                           = -1;
static int hf_btvdp_codec                              = -1;
static int hf_btvdp_vendor_id                          = -1;
static int hf_btvdp_vendor_codec_id                    = -1;
static int hf_btvdp_content_protection                 = -1;
static int hf_btvdp_stream_start_in_frame              = -1;
static int hf_btvdp_stream_end_in_frame                = -1;
static int hf_btvdp_stream_number                      = -1;
static int hf_btvdp_l_bit                              = -1;
static int hf_btvdp_cp_bit                             = -1;
static int hf_btvdp_reserved                           = -1;

static dissector_handle_t h263_handle;
static dissector_handle_t mp4v_es_handle;

static gboolean  force_vdp_scms_t = FALSE;
static gint      force_vdp_codec = CODEC_H263_BASELINE;

static const enum_val_t pref_vdp_codec[] = {
    { "h263",    "H263",      CODEC_H263_BASELINE },
    { "mp4v-es", "MPEG4 VSP", CODEC_MPEG4_VSP },
    { NULL, NULL, 0 }
};

/* APT-X Codec */
static int  proto_aptx                            = -1;
static int  hf_aptx_data                          = -1;
static int  hf_aptx_cumulative_frame_duration    = -1;
static int  hf_aptx_delta_time                    = -1;
static int  hf_aptx_avrcp_song_position           = -1;
static int  hf_aptx_delta_time_from_the_beginning = -1;
static int  hf_aptx_cumulative_duration          = -1;
static int  hf_aptx_diff                          = -1;
static gint ett_aptx                              = -1;
static dissector_handle_t aptx_handle;

/* LDAC Codec */
static int  proto_ldac                            = -1;
static int  hf_ldac_fragmented                    = -1;
static int  hf_ldac_starting_packet               = -1;
static int  hf_ldac_last_packet                   = -1;
static int  hf_ldac_rfa                           = -1;
static int  hf_ldac_number_of_frames              = -1;

static int hf_ldac_syncword                       = -1;
static int hf_ldac_sampling_frequency             = -1;
static int hf_ldac_channel_config_index           = -1;
static int hf_ldac_frame_length_h                 = -1;
static int hf_ldac_frame_length_l                 = -1;
static int hf_ldac_frame_status                   = -1;

static int hf_ldac_expected_data_speed            = -1;

static int  hf_ldac_data                          = -1;
static gint ett_ldac                              = -1;
static gint ett_ldac_list                         = -1;
static expert_field ei_ldac_syncword = EI_INIT;
static expert_field ei_ldac_truncated_or_bad_length = EI_INIT;
static dissector_handle_t ldac_handle;
#define LDAC_CCI_MONO   0x0
#define LDAC_CCI_DUAL   0x1
#define LDAC_CCI_STEREO 0x2
static const value_string ldac_channel_config_index_vals[] = {
    { LDAC_CCI_MONO,  "Mono"},
    { LDAC_CCI_DUAL,  "Dual Channel"},
    { LDAC_CCI_STEREO,  "Stereo"},
    { 0, NULL }
};

#define LDAC_FSID_044       0x0
#define LDAC_FSID_048       0x1
#define LDAC_FSID_088       0x2
#define LDAC_FSID_096       0x3
#define LDAC_FSID_176       0x4
#define LDAC_FSID_192       0x5

static const value_string ldac_sampling_frequency_vals[] = {
    { LDAC_FSID_044,  "44.1 kHz"},
    { LDAC_FSID_048,  "48.0 kHz"},
    { LDAC_FSID_088,  "88.2 kHz"},
    { LDAC_FSID_096,  "96.0 kHz"},
    { LDAC_FSID_176,  "176.4 kHz"},
    { LDAC_FSID_192,  "192.0 kHz"},
    { 0, NULL }
};


static const value_string message_type_vals[] = {
    { 0x00,  "Command" },
    { 0x01,  "GeneralReject" },
    { 0x02,  "ResponseAccept" },
    { 0x03,  "ResponseReject" },
    { 0, NULL }
};

static const value_string packet_type_vals[] = {
    { 0x00,  "Single" },
    { 0x01,  "Start" },
    { 0x02,  "Continue" },
    { 0x03,  "End" },
    { 0, NULL }
};

static const value_string signal_id_vals[] = {
    { 0x00, "Reserved" },
    { 0x01, "Discover" },
    { 0x02, "GetCapabilities" },
    { 0x03, "SetConfiguration" },
    { 0x04, "GetConfiguration" },
    { 0x05, "Reconfigure" },
    { 0x06, "Open" },
    { 0x07, "Start" },
    { 0x08, "Close" },
    { 0x09, "Suspend" },
    { 0x0A, "Abort" },
    { 0x0B, "SecurityControl" },
    { 0x0C, "GetAllCapabilities" },
    { 0x0D, "DelayReport" },
    { 0, NULL }
};

static const value_string media_type_vals[] = {
    { 0x00,  "Audio" },
    { 0x01,  "Video" },
    { 0x02,  "Multimedia" },
    { 0, NULL }
};

static const value_string sep_type_vals[] = {
    { 0x00,  "Source" },
    { 0x01,  "Sink" },
    { 0, NULL }
};

static const value_string true_false[] = {
    { 0x00,  "False" },
    { 0x01,  "True" },
    { 0, NULL }
};

static const value_string error_code_vals[] = {
    /* ACP to INT, Signal Response Header Error Codes */
    { 0x01,  "Bad Header Format" },
    /* ACP to INT, Signal Response Payload Format Error Codes */
    { 0x11,  "Bad Length" },
    { 0x12,  "Bad ACP SEID" },
    { 0x13,  "SEP In Use" },
    { 0x14,  "SEP Not In Use" },
    { 0x17,  "Bad Service Category" },
    { 0x18,  "Bad Payload Format" },
    { 0x19,  "Not Supported Command" },
    { 0x1A,  "Invalid Capabilities" },
    /* ACP to INT, Signal Response Transport Service Capabilities Error Codes */
    { 0x22,  "Bad Recovery Type" },
    { 0x23,  "Bad Media Transport Format" },
    { 0x25,  "Bad Recovery Format" },
    { 0x26,  "Bad Header Compression Format" },
    { 0x27,  "Bad Content Protection Format" },
    { 0x28,  "Bad Multiplexing Format" },
    { 0x29,  "Unsupported Configuration" },
    /* ACP to INT, Procedure Error Codes */
    { 0x31,  "Bad State" },
    /* GAVDTP */
    { 0x80,  "The Service Category Stated is Invalid" },
    { 0x81,  "Lack of Resource New Stream Context" },
    /* A2DP */
    { 0xC1,  "Invalid Codec Type" },
    { 0xC2,  "Not Supported Codec Type" },
    { 0xC3,  "Invalid Sampling Frequency" },
    { 0xC4,  "Not Supported Sampling Frequency" },
    { 0xC5,  "Invalid Channel Mode" },
    { 0xC6,  "Not Supported Channel Mode" },
    { 0xC7,  "Invalid Subbands" },
    { 0xC8,  "Not Supported Subbands" },
    { 0xC9,  "Invalid Allocation Method" },
    { 0xCA,  "Not Supported Allocation Method" },
    { 0xCB,  "Invalid Minimum Bitpool Value" },
    { 0xCC,  "Not Supported Minimum Bitpool Value" },
    { 0xCD,  "Invalid Maximum Bitpool Value" },
    { 0xCE,  "Not Supported Maximum Bitpool Value" },
    { 0xCF,  "Invalid Layer" },
    { 0xD0,  "Not Supported Layer" },
    { 0xD1,  "Not Supported CRC" },
    { 0xD2,  "Not Supported MPF" },
    { 0xD3,  "Not Supported VBR" },
    { 0xD4,  "Invalid Bit Rate" },
    { 0xD5,  "Not Supported Bit Rate" },
    { 0xD6,  "Invalid Object Type" },
    { 0xD7,  "Not Supported Object Type" },
    { 0xD8,  "Invalid Channels" },
    { 0xD9,  "Not Supported Channels" },
    { 0xDA,  "Invalid Version" },
    { 0xDB,  "Not Supported Version" },
    { 0xDC,  "Not Supported Maximum SUL" },
    { 0xDD,  "Invalid Block Length" },
    { 0xE0,  "Invalid Content Protection Type" },
    { 0xE1,  "Invalid Content Protection Format" },
    { 0xE2,  "Invalid Coded Parameter" },
    { 0xE3,  "Not Supported Codec Parameter" },
    { 0, NULL }
};

static const value_string service_category_vals[] = {
    { 0x01,  "Media Transport" },
    { 0x02,  "Reporting" },
    { 0x03,  "Recovery" },
    { 0x04,  "Content Protection" },
    { 0x05,  "Header Compression" },
    { 0x06,  "Multiplexing" },
    { 0x07,  "Media Codec" },
    { 0x08,  "Delay Reporting" },
    { 0, NULL }
};

static const value_string recovery_type_vals[] = {
    { 0x00,  "Forbidden" },
    { 0x01,  "RFC2733" },
    { 0, NULL }
};

static const value_string multiplexing_tsid_vals[] = {
    { 0x00,  "Used for TSID query" },
    { 0x1F,  "RFD" },
    { 0, NULL }
};

static const value_string multiplexing_tcid_vals[] = {
    { 0x00,  "Used for TCID query" },
    { 0x1F,  "RFD" },
    { 0, NULL }
};

static const value_string media_codec_audio_type_vals[] = {
    { 0x00,  "SBC" },
    { 0x01,  "MPEG-1,2 Audio" },
    { 0x02,  "MPEG-2,4 AAC" },
    { 0x04,  "ATRAC family" },
    { 0xFF,  "non-A2DP" },
    { 0, NULL }
};

static const value_string media_codec_video_type_vals[] = {
    { 0x01,  "H.263 baseline" },
    { 0x02,  "MPEG-4 Visual Simple Profile" },
    { 0x03,  "H.263 profile 3" },
    { 0x04,  "H.263 profile 8" },
    { 0xFF,  "non-VDP" },
    { 0, NULL }
};

static const value_string content_protection_type_vals[] = {
    { 0x01,  "DTCP" },
    { 0x02,  "SCMS-T" },
    { 0, NULL }
};

static const value_string vendor_apt_codec_vals[] = {
    { CODECID_APT_X,     "aptX" },
    { CODECID_APT_X_HD,  "aptX HD" },
    { 0, NULL }
};

static const value_string vendor_ldac_codec_vals[] = {
    { 0x00AA,  "LDAC" },
    { 0, NULL }
};

enum sep_state {
    SEP_STATE_FREE,
    SEP_STATE_OPEN,
    SEP_STATE_IN_USE
};

typedef struct _sep_entry_t {
    guint8         seid;
    guint8         type;
    guint8         media_type;
    guint8         int_seid;
    gint           codec;
    guint32        vendor_id;
    guint16        vendor_codec;
    guint8         configuration_length;
    guint8        *configuration;
    gint           content_protection_type;

    enum sep_state state;
} sep_entry_t;

typedef struct _sep_data_t {
    gint      codec;
    guint32   vendor_id;
    guint16   vendor_codec;
    guint8    configuration_length;
    guint8   *configuration;
    guint8    acp_seid;
    guint8    int_seid;
    gint      content_protection_type;
    guint32   stream_start_in_frame;
    guint32   stream_end_in_frame;
    guint32   stream_number;
    media_packet_info_t  *previous_media_packet_info;
    media_packet_info_t  *current_media_packet_info;
} sep_data_t;

typedef struct _media_stream_number_value_t {
    guint32      stream_start_in_frame;
    guint32      stream_end_in_frame;
    guint32      stream_number;
} media_stream_number_value_t;

typedef struct _channels_info_t {
    guint32       control_local_cid;
    guint32       control_remote_cid;
    guint32       media_local_cid;
    guint32       media_remote_cid;
    wmem_tree_t  *stream_numbers;
    guint32       disconnect_in_frame;
    guint32      *l2cap_disconnect_in_frame;
    guint32      *hci_disconnect_in_frame;
    guint32      *adapter_disconnect_in_frame;
    sep_entry_t  *sep;
} channels_info_t;


void proto_register_btavdtp(void);
void proto_reg_handoff_btavdtp(void);
void proto_register_bta2dp(void);
void proto_reg_handoff_bta2dp(void);
void proto_register_bta2dp_content_protection_header_scms_t(void);
void proto_register_btvdp(void);
void proto_reg_handoff_btvdp(void);
void proto_register_btvdp_content_protection_header_scms_t(void);
void proto_register_aptx(void);
void proto_register_ldac(void);


static const char *
get_sep_type(guint32 interface_id,
    guint32 adapter_id, guint32 chandle, guint32 direction, guint32 seid, guint32 frame_number)
{
    wmem_tree_key_t   key[6];
    wmem_tree_t      *subtree;
    sep_entry_t      *sep;

    key[0].length = 1;
    key[0].key    = &interface_id;
    key[1].length = 1;
    key[1].key    = &adapter_id;
    key[2].length = 1;
    key[2].key    = &chandle;
    key[3].length = 1;
    key[3].key    = &direction;
    key[4].length = 1;
    key[4].key    = &seid;
    key[5].length = 0;
    key[5].key    = NULL;

    subtree = (wmem_tree_t *) wmem_tree_lookup32_array(sep_list, key);
    sep = (subtree) ? (sep_entry_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
    if (sep) {
        return val_to_str_const(sep->type, sep_type_vals, "unknown");
    }

    return "unknown";
}

static const char *
get_sep_media_type(guint32 interface_id,
    guint32 adapter_id, guint32 chandle, guint32 direction, guint32 seid, guint32 frame_number)
{
    wmem_tree_key_t   key[6];
    wmem_tree_t      *subtree;
    sep_entry_t      *sep;

    key[0].length = 1;
    key[0].key    = &interface_id;
    key[1].length = 1;
    key[1].key    = &adapter_id;
    key[2].length = 1;
    key[2].key    = &chandle;
    key[3].length = 1;
    key[3].key    = &direction;
    key[4].length = 1;
    key[4].key    = &seid;
    key[5].length = 0;
    key[5].key    = NULL;

    subtree = (wmem_tree_t *) wmem_tree_lookup32_array(sep_list, key);
    sep = (subtree) ? (sep_entry_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
    if (sep) {
        return val_to_str_const(sep->media_type, media_type_vals, "unknown");
    }

    return "unknown";
}


static gint
dissect_sep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset,
    guint32 interface_id, guint32 adapter_id, guint32 chandle)
{
    proto_tree       *sep_tree;
    proto_item       *sep_item;
    guint            i_sep  = 1;
    guint            media_type;
    guint            type;
    guint            seid;
    guint            in_use;
    guint            items;
    guint32          direction;

    /* Reverse direction to avoid mass reversing it, because this is only case
       when SEP is provided in ACP role, otherwise INT frequently asking for it
    */
    direction = (pinfo->p2p_dir == P2P_DIR_SENT) ? P2P_DIR_RECV : P2P_DIR_SENT;
    items = tvb_reported_length_remaining(tvb, offset) / 2;
    while (tvb_reported_length_remaining(tvb, offset) > 0) {
        seid = tvb_get_guint8(tvb, offset);
        in_use = seid & 0x02;
        seid = seid >> 2;
        media_type = tvb_get_guint8(tvb, offset + 1) >> 4;
        type = (tvb_get_guint8(tvb, offset + 1) & 0x08) >> 3;
        sep_item = proto_tree_add_none_format(tree, hf_btavdtp_acp_sep, tvb, offset, 2, "ACP SEP [%u - %s %s] item %u/%u",
                seid, val_to_str_const(media_type, media_type_vals, "unknown"),
                val_to_str_const(type, sep_type_vals, "unknown"), i_sep, items);
        sep_tree = proto_item_add_subtree(sep_item, ett_btavdtp_sep);

        proto_tree_add_item(sep_tree, hf_btavdtp_sep_seid , tvb, offset, 1, ENC_NA);
        proto_tree_add_item(sep_tree, hf_btavdtp_sep_inuse, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(sep_tree, hf_btavdtp_sep_rfa0 , tvb, offset, 1, ENC_NA);
        offset+=1;

        proto_tree_add_item(sep_tree, hf_btavdtp_sep_media_type, tvb, offset, 1, ENC_NA);
        proto_tree_add_item(sep_tree, hf_btavdtp_sep_type      , tvb, offset, 1, ENC_NA);
        proto_tree_add_item(sep_tree, hf_btavdtp_sep_rfa1      , tvb, offset, 1, ENC_NA);

        if (!pinfo->fd->visited) {
            sep_entry_t     *sep_data;
            wmem_tree_key_t  key[7];
            guint32          frame_number = pinfo->num;

            key[0].length = 1;
            key[0].key    = &interface_id;
            key[1].length = 1;
            key[1].key    = &adapter_id;
            key[2].length = 1;
            key[2].key    = &chandle;
            key[3].length = 1;
            key[3].key    = &direction;
            key[4].length = 1;
            key[4].key    = &seid;
            key[5].length = 1;
            key[5].key    = &frame_number;
            key[6].length = 0;
            key[6].key    = NULL;

            sep_data = wmem_new0(wmem_file_scope(), sep_entry_t);
            sep_data->seid = seid;
            sep_data->type = type;
            sep_data->media_type = media_type;
            sep_data->codec = -1;
            if (in_use) {
                sep_data->state = SEP_STATE_IN_USE;
            } else {
                sep_data->state = SEP_STATE_FREE;
            }

            wmem_tree_insert32_array(sep_list, key, sep_data);
        }

        offset += 1;
        i_sep += 1;
    }

    col_append_fstr(pinfo->cinfo, COL_INFO, " - items: %u", items);
    return offset;
}


static gint
dissect_codec(tvbuff_t *tvb, packet_info *pinfo, proto_item *service_item, proto_tree *tree, gint offset,
        guint losc, gint media_type, gint media_codec_type, guint32 *vendor_id, guint16 *vendor_codec)
{
    proto_item    *pitem;
    guint32        value;
    guint8        *value8 = (guint8 *) &value;

    switch(media_type) {
        case MEDIA_TYPE_AUDIO:
            switch(media_codec_type) {
                case CODEC_SBC:
                    proto_tree_add_item(tree, hf_btavdtp_sbc_sampling_frequency_16000, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_sampling_frequency_32000, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_sampling_frequency_44100, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_sampling_frequency_48000, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_channel_mode_mono, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_channel_mode_dual_channel, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_channel_mode_stereo, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_channel_mode_joint_stereo, tvb, offset, 1, ENC_NA);

                    proto_tree_add_item(tree, hf_btavdtp_sbc_block_4, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_block_8, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_block_12, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_block_16, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_subbands_4, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_subbands_8, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_allocation_method_snr, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_sbc_allocation_method_loudness, tvb, offset + 1, 1, ENC_NA);

                    pitem = proto_tree_add_item(tree, hf_btavdtp_sbc_min_bitpool, tvb, offset + 2, 1, ENC_NA);
                    value = tvb_get_guint8(tvb, offset + 2);
                    if (value < 2 || value > 250) {
                        expert_add_info(pinfo, pitem, &ei_btavdtp_sbc_min_bitpool_out_of_range);
                    }

                    pitem = proto_tree_add_item(tree, hf_btavdtp_sbc_max_bitpool, tvb, offset + 3, 1, ENC_NA);
                    value = tvb_get_guint8(tvb, offset + 3);
                    if (value < 2 || value > 250) {
                        expert_add_info(pinfo, pitem, &ei_btavdtp_sbc_max_bitpool_out_of_range);
                    }

                    value = tvb_get_h_guint32(tvb, offset);
                    if (value) {
                        col_append_fstr(pinfo->cinfo, COL_INFO, " (%s%s%s%s%s| %s%s%s%s%s| block: %s%s%s%s%s| subbands: %s%s%s| allocation: %s%s%s| bitpool: %u..%u)",
                            (value8[0] & 0x80) ? "16000 " : "",
                            (value8[0] & 0x40) ? "32000 " : "",
                            (value8[0] & 0x20) ? "44100 " : "",
                            (value8[0] & 0x10) ? "48000 " : "",
                            (value8[0] & 0xF0) ? "" : "not set ",
                            (value8[0] & 0x08) ? "Mono " : "",
                            (value8[0] & 0x04) ? "DualChannel " : "",
                            (value8[0] & 0x02) ? "Stereo " : "",
                            (value8[0] & 0x01) ? "JointStereo " : "",
                            (value8[0] & 0x0F) ? "" : "not set ",
                            (value8[1] & 0x80) ? "4 " : "",
                            (value8[1] & 0x40) ? "8 " : "",
                            (value8[1] & 0x20) ? "12 " : "",
                            (value8[1] & 0x10) ? "16 " : "",
                            (value8[1] & 0xF0) ? "" : "not set ",
                            (value8[1] & 0x08) ? "4 " : "",
                            (value8[1] & 0x04) ? "8 " : "",
                            (value8[1] & 0x0C) ? "" : "not set ",
                            (value8[1] & 0x02) ? "SNR " : "",
                            (value8[1] & 0x01) ? "Loudness " : "",
                            (value8[1] & 0x03) ? "" : "not set ",
                            value8[2],
                            value8[3]);

                        proto_item_append_text(service_item, " (%s%s%s%s%s| %s%s%s%s%s| block: %s%s%s%s%s| subbands: %s%s%s| allocation: %s%s%s| bitpool: %u..%u)",
                            (value8[0] & 0x80) ? "16000 " : "",
                            (value8[0] & 0x40) ? "32000 " : "",
                            (value8[0] & 0x20) ? "44100 " : "",
                            (value8[0] & 0x10) ? "48000 " : "",
                            (value8[0] & 0xF0) ? "" : "not set ",
                            (value8[0] & 0x08) ? "Mono " : "",
                            (value8[0] & 0x04) ? "DualChannel " : "",
                            (value8[0] & 0x02) ? "Stereo " : "",
                            (value8[0] & 0x01) ? "JointStereo " : "",
                            (value8[0] & 0x0F) ? "" : "not set ",
                            (value8[1] & 0x80) ? "4 " : "",
                            (value8[1] & 0x40) ? "8 " : "",
                            (value8[1] & 0x20) ? "12 " : "",
                            (value8[1] & 0x10) ? "16 " : "",
                            (value8[1] & 0xF0) ? "" : "not set ",
                            (value8[1] & 0x08) ? "4 " : "",
                            (value8[1] & 0x04) ? "8 " : "",
                            (value8[1] & 0x0C) ? "" : "not set ",
                            (value8[1] & 0x02) ? "SNR " : "",
                            (value8[1] & 0x01) ? "Loudness " : "",
                            (value8[1] & 0x03) ? "" : "not set ",
                            value8[2],
                            value8[3]);
                    } else {
                        col_append_fstr(pinfo->cinfo, COL_INFO, " (none)");
                        proto_item_append_text(service_item, " (none)");
                    }

                    break;
                case CODEC_MPEG12_AUDIO:
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_layer_1, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_layer_2, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_layer_3, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_crc_protection, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_channel_mode_mono, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_channel_mode_dual_channel, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_channel_mode_stereo, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_channel_mode_joint_stereo, tvb, offset, 1, ENC_NA);

                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_rfa, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_mpf_2, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_sampling_frequency_16000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_sampling_frequency_22050, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_sampling_frequency_24000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_sampling_frequency_32000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_sampling_frequency_44100, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_sampling_frequency_48000, tvb, offset + 1, 1, ENC_NA);

                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_vbr_supported, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg12_bit_rate, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
                    break;
                case CODEC_MPEG24_AAC:
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_object_type_mpeg2_aac_lc, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_object_type_mpeg4_aac_lc, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_object_type_mpeg4_aac_ltp, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_object_type_mpeg4_aac_scalable, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_object_type_rfa, tvb, offset, 1, ENC_NA);

                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_8000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_11025, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_12000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_16000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_22050, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_24000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_32000, tvb, offset + 1, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_44100, tvb, offset + 1, 1, ENC_NA);

                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_48000, tvb, offset + 2, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_64000, tvb, offset + 2, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_88200, tvb, offset + 2, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_sampling_frequency_96000, tvb, offset + 2, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_channels_1, tvb, offset + 2, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_channels_2, tvb, offset + 2, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_rfa, tvb, offset + 2, 1, ENC_NA);

                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_vbr_supported, tvb, offset + 3, 3, ENC_BIG_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg24_bit_rate, tvb, offset + 3, 3, ENC_BIG_ENDIAN);
                    break;
                case CODEC_ATRAC:
                    proto_tree_add_item(tree, hf_btavdtp_atrac_version, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_channel_mode_single_channel, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_channel_mode_dual_channel, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_channel_mode_joint_stereo, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_rfa1, tvb, offset, 1, ENC_NA);

                    proto_tree_add_item(tree, hf_btavdtp_atrac_rfa2, tvb, offset + 1, 3, ENC_BIG_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_sampling_frequency_44100, tvb, offset + 1, 3, ENC_BIG_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_sampling_frequency_48000, tvb, offset + 1, 3, ENC_BIG_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_vbr_supported, tvb, offset + 3, 3, ENC_BIG_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_atrac_bit_rate, tvb, offset + 3, 3, ENC_BIG_ENDIAN);

                    proto_tree_add_item(tree, hf_btavdtp_atrac_maximum_sul, tvb, offset + 4, 2, ENC_BIG_ENDIAN);

                    proto_tree_add_item(tree, hf_btavdtp_atrac_rfa3, tvb, offset + 6, 1, ENC_NA);
                    break;
                case CODEC_VENDOR: /* non-A2DP */
                    proto_tree_add_item(tree, hf_btavdtp_vendor_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);

                    if (vendor_id)
                        *vendor_id = tvb_get_letohl(tvb, offset);

                    if (vendor_codec)
                        *vendor_codec = tvb_get_letohs(tvb, offset + 4);

                    switch (tvb_get_letohl(tvb, offset)) {
                        case 0x004F: /* APT Licensing Ltd. */
                        case 0x00D7: /* Qualcomm technologies, Inc. */
                            proto_tree_add_item(tree, hf_btavdtp_vendor_specific_apt_codec_id, tvb, offset + 4, 2, ENC_LITTLE_ENDIAN);
                            value = tvb_get_letohs(tvb, offset + 4);

                            if (value == CODECID_APT_X || value == CODECID_APT_X_HD) { /* APT-X or APT-X HD Codec */
                                if (value == CODECID_APT_X) {
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_sampling_frequency_16000, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_sampling_frequency_32000, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_sampling_frequency_44100, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_sampling_frequency_48000, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_channel_mode_mono, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_channel_mode_dual_channel, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_channel_mode_stereo, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptx_channel_mode_joint_stereo, tvb, offset + 6, 1, ENC_NA);
                                } else {
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_16000, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_32000, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_44100, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_48000, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_channel_mode_mono, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_channel_mode_dual_channel, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_channel_mode_stereo, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_channel_mode_joint_stereo, tvb, offset + 6, 1, ENC_NA);
                                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_aptxhd_rfa, tvb, offset + 7, 4, ENC_NA);
                                }

                                col_append_fstr(pinfo->cinfo, COL_INFO, " (%s -",
                                    val_to_str_const(value, vendor_apt_codec_vals, "unknown codec"));
                                proto_item_append_text(service_item, " (%s -",
                                    val_to_str_const(value, vendor_apt_codec_vals, "unknown codec"));

                                value = tvb_get_guint8(tvb, offset + 6);
                                if (value) {
                                    col_append_fstr(pinfo->cinfo, COL_INFO, "%s%s%s%s%s,%s%s%s%s%s)",
                                        (value & 0x80) ? " 16000" : "",
                                        (value & 0x40) ? " 32000" : "",
                                        (value & 0x20) ? " 44100" : "",
                                        (value & 0x10) ? " 48000" : "",
                                        (value & 0xF0) ? "" : "not set ",
                                        (value & 0x08) ? " Mono" : "",
                                        (value & 0x04) ? " DualChannel" : "",
                                        (value & 0x02) ? " Stereo" : "",
                                        (value & 0x01) ? " JointStereo" : "",
                                        (value & 0x0F) ? "" : "not set ");

                                    proto_item_append_text(service_item, "%s%s%s%s%s,%s%s%s%s%s)",
                                        (value & 0x80) ? " 16000" : "",
                                        (value & 0x40) ? " 32000" : "",
                                        (value & 0x20) ? " 44100" : "",
                                        (value & 0x10) ? " 48000" : "",
                                        (value & 0xF0) ? "" : "not set ",
                                        (value & 0x08) ? " Mono" : "",
                                        (value & 0x04) ? " DualChannel" : "",
                                        (value & 0x02) ? " Stereo" : "",
                                        (value & 0x01) ? " JointStereo" : "",
                                        (value & 0x0F) ? "" : "not set ");
                                } else {
                                    col_append_fstr(pinfo->cinfo, COL_INFO, " none)");
                                    proto_item_append_text(service_item, " none)");
                                }
                            } else {
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_value, tvb, offset + 6, losc - 6, ENC_NA);
                            }
                            break;
                        case 0x012D: /* Sony Corporation. */
                            proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_codec_id, tvb, offset + 4, 2, ENC_LITTLE_ENDIAN);
                            value = tvb_get_letohs(tvb, offset + 4);

                            if (value == 0x00AA) { /* LDAC Codec */
                                int value2;
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_rfa1, tvb, offset + 6, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_sampling_frequency_44100, tvb, offset + 6, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_sampling_frequency_48000, tvb, offset + 6, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_sampling_frequency_88200, tvb, offset + 6, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_sampling_frequency_96000, tvb, offset + 6, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_sampling_frequency_176400, tvb, offset + 6, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_sampling_frequency_192000, tvb, offset + 6, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_rfa2, tvb, offset + 7, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_channel_mode_mono, tvb, offset + 7, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_channel_mode_dual_channel, tvb, offset + 7, 1, ENC_NA);
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_ldac_channel_mode_stereo, tvb, offset + 7, 1, ENC_NA);

                                col_append_fstr(pinfo->cinfo, COL_INFO, " (%s -",
                                    val_to_str_const(value, vendor_ldac_codec_vals, "unknown codec"));
                                proto_item_append_text(service_item, " (%s -",
                                    val_to_str_const(value, vendor_ldac_codec_vals, "unknown codec"));

                                value = tvb_get_guint8(tvb, offset + 6);
                                value2 = tvb_get_guint8(tvb, offset + 7);
                                if (value != 0 && value2 != 0) {
                                    col_append_fstr(pinfo->cinfo, COL_INFO, "%s%s%s%s%s%s,%s%s%s)",
                                        (value & 0x20) ? " 44100" : "",
                                        (value & 0x10) ? " 48000" : "",
                                        (value & 0x08) ? " 88200" : "",
                                        (value & 0x04) ? " 96000" : "",
                                        (value & 0x02) ? "176400" : "",
                                        (value & 0x01) ? "192000" : "",
                                        (value2 & 0x04) ? " Mono" : "",
                                        (value2 & 0x02) ? " DualChannel" : "",
                                        (value2 & 0x01) ? " Stereo" : "");

                                    proto_item_append_text(service_item, "%s%s%s%s%s%s,%s%s%s)",
                                        (value & 0x20) ? " 44100" : "",
                                        (value & 0x10) ? " 48000" : "",
                                        (value & 0x08) ? " 88200" : "",
                                        (value & 0x04) ? " 96000" : "",
                                        (value & 0x02) ? "176400" : "",
                                        (value & 0x01) ? "192000" : "",
                                        (value2 & 0x04) ? " Mono" : "",
                                        (value2 & 0x02) ? " DualChannel" : "",
                                        (value2 & 0x01) ? " Stereo" : "");
                                } else {
                                    col_append_fstr(pinfo->cinfo, COL_INFO, " none)");
                                    proto_item_append_text(service_item, " none)");
                                }
                            } else {
                                proto_tree_add_item(tree, hf_btavdtp_vendor_specific_value, tvb, offset + 6, losc - 6, ENC_NA);
                            }
                            break;
                        default:
                            proto_tree_add_item(tree, hf_btavdtp_vendor_specific_codec_id, tvb, offset + 4, 2, ENC_LITTLE_ENDIAN);
                            proto_tree_add_item(tree, hf_btavdtp_vendor_specific_value, tvb, offset + 6, losc - 6, ENC_NA);
                    }

                    break;
                default:
                    proto_tree_add_item(tree, hf_btavdtp_data, tvb, offset, losc, ENC_NA);
            }
            break;
        case MEDIA_TYPE_VIDEO:
            switch(media_codec_type) {
                case CODEC_H263_BASELINE:
                case CODEC_H263_PROFILE_3:
                case CODEC_H263_PROFILE_8:
                    proto_tree_add_item(tree, hf_btavdtp_h263_level_10, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_h263_level_20, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_h263_level_30, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_h263_level_rfa, tvb, offset, 1, ENC_NA);
                    break;
                case CODEC_MPEG4_VSP:
                    proto_tree_add_item(tree, hf_btavdtp_mpeg4_level_0, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg4_level_1, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg4_level_2, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg4_level_3, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(tree, hf_btavdtp_mpeg4_level_rfa, tvb, offset, 1, ENC_NA);
                    break;
                case CODEC_VENDOR: /* non-VDP */
                    proto_tree_add_item(tree, hf_btavdtp_vendor_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_codec_id, tvb, offset + 4, 2, ENC_LITTLE_ENDIAN);
                    proto_tree_add_item(tree, hf_btavdtp_vendor_specific_value, tvb, offset + 6, losc - 6, ENC_NA);
                    break;
                default:
                    proto_tree_add_item(tree, hf_btavdtp_data, tvb, offset, losc, ENC_NA);
            }
            break;
        default:
            proto_tree_add_item(tree, hf_btavdtp_data, tvb, offset, losc, ENC_NA);
    }

    offset += losc;

    return offset;
}


static gint
dissect_capabilities(tvbuff_t *tvb, packet_info *pinfo,
        proto_tree *tree, gint offset, gint *codec,
        gint *content_protection_type, guint32 *vendor_id,
        guint16 *vendor_codec, guint32 *configuration_offset,
        guint8 *configuration_length)
{
    proto_item  *pitem                                        = NULL;
    proto_item  *ptree                                        = NULL;
    proto_tree  *capabilities_tree;
    proto_item  *capabilities_item;
    proto_tree  *service_tree                                 = NULL;
    proto_item  *service_item                                 = NULL;
    gint        service_category                              = 0;
    gint        losc                                          = 0;
    gint        recovery_type                                 = 0;
    gint        maximum_recovery_window_size                  = 0;
    gint        maximum_number_of_media_packet_in_parity_code = 0;
    gint        media_type                                    = 0;
    gint        media_codec_type                              = 0;

    capabilities_item = proto_tree_add_item(tree, hf_btavdtp_capabilities, tvb, offset, tvb_reported_length(tvb) - offset, ENC_NA);
    capabilities_tree = proto_item_add_subtree(capabilities_item, ett_btavdtp_capabilities);

    if (codec)
        *codec = -1;

    if (vendor_id)
        *vendor_id = 0x003F; /* Bluetooth SIG */

    if (vendor_codec)
        *vendor_codec = 0;

    if (configuration_length)
        *configuration_length = 0;

    if (configuration_offset)
        *configuration_offset = 0;

    while (tvb_reported_length_remaining(tvb, offset) > 0) {
        service_category = tvb_get_guint8(tvb, offset);
        losc = tvb_get_guint8(tvb, offset + 1);
        service_item = proto_tree_add_none_format(capabilities_tree, hf_btavdtp_service, tvb, offset, 2 + losc, "Service: %s", val_to_str_const(service_category, service_category_vals, "RFD"));
        service_tree = proto_item_add_subtree(service_item, ett_btavdtp_service);

        proto_tree_add_item(service_tree, hf_btavdtp_service_category, tvb, offset, 1, ENC_NA);
        offset += 1;

        proto_tree_add_item(service_tree, hf_btavdtp_length_of_service_category, tvb, offset, 1, ENC_NA);
        offset += 1;

        switch (service_category) {
            case SERVICE_CATEGORY_MEDIA_TRANSPORT:
            case SERVICE_CATEGORY_REPORTING:
            case SERVICE_CATEGORY_DELAY_REPORTING:
                /* losc should be 0 */
                break;
            case SERVICE_CATEGORY_RECOVERY:
                recovery_type = tvb_get_guint8(tvb, offset);
                pitem = proto_tree_add_item(service_tree, hf_btavdtp_recovery_type, tvb, offset, 1, ENC_NA);
                proto_item_append_text(pitem, " (%s)", val_to_str_const(recovery_type, recovery_type_vals, "RFD"));
                offset += 1;
                losc -= 1;

                maximum_recovery_window_size = tvb_get_guint8(tvb, offset);
                pitem = proto_tree_add_item(service_tree, hf_btavdtp_maximum_recovery_window_size, tvb, offset, 1, ENC_NA);
                if (maximum_recovery_window_size == 0x00) {
                    proto_item_append_text(pitem, " (Forbidden)");
                } else if (maximum_recovery_window_size >= 0x18) {
                    proto_item_append_text(pitem, " (Undocumented)");
                }
                offset += 1;
                losc -= 1;

                maximum_number_of_media_packet_in_parity_code = tvb_get_guint8(tvb, offset);
                proto_tree_add_item(service_tree, hf_btavdtp_maximum_number_of_media_packet_in_parity_code, tvb, offset, 1, ENC_NA);
                pitem = proto_tree_add_item(service_tree, hf_btavdtp_maximum_recovery_window_size, tvb, offset, 1, ENC_NA);
                if (maximum_number_of_media_packet_in_parity_code == 0x00) {
                    proto_item_append_text(pitem, " (Forbidden)");
                } else if (maximum_number_of_media_packet_in_parity_code >= 0x18) {
                    proto_item_append_text(pitem, " (Undocumented)");
                }
                offset += 1;
                losc -= 1;
                break;
            case SERVICE_CATEGORY_MEDIA_CODEC:
                if (configuration_length)
                    *configuration_length = losc;
                if (configuration_offset)
                    *configuration_offset = offset;

                media_type = tvb_get_guint8(tvb, offset) >> 4;
                proto_tree_add_item(service_tree, hf_btavdtp_media_codec_media_type, tvb, offset, 1, ENC_NA);
                proto_tree_add_item(service_tree, hf_btavdtp_media_codec_rfa , tvb, offset, 1, ENC_NA);
                offset += 1;
                losc -= 1;

                media_codec_type = tvb_get_guint8(tvb, offset);
                if (codec) {
                    *codec = media_codec_type;
                }

                if (media_type == MEDIA_TYPE_AUDIO) {
                    proto_tree_add_item(service_tree, hf_btavdtp_media_codec_audio_type, tvb, offset, 1, ENC_NA);
                    proto_item_append_text(service_item, " - Audio %s",
                            val_to_str_const(media_codec_type, media_codec_audio_type_vals, "unknown codec"));
                    col_append_fstr(pinfo->cinfo, COL_INFO, " - Audio %s",
                            val_to_str_const(media_codec_type, media_codec_audio_type_vals, "unknown codec"));
                } else if (media_type == MEDIA_TYPE_VIDEO) {
                    proto_tree_add_item(service_tree, hf_btavdtp_media_codec_video_type, tvb, offset, 1, ENC_NA);
                    proto_item_append_text(service_item, " - Video %s",
                            val_to_str_const(media_codec_type, media_codec_video_type_vals, "unknown codec"));
                    col_append_fstr(pinfo->cinfo, COL_INFO, " - Video %s",
                            val_to_str_const(media_codec_type, media_codec_video_type_vals, "unknown codec"));
                } else {
                    proto_tree_add_item(service_tree, hf_btavdtp_media_codec_unknown_type, tvb, offset, 1, ENC_NA);
                    proto_item_append_text(service_item, " - Unknown 0x%02x", media_codec_type);
                    col_append_fstr(pinfo->cinfo, COL_INFO, " - Unknown 0x%02x", media_codec_type);
                }
                offset += 1;
                losc -= 1;

                offset = dissect_codec(tvb, pinfo, service_item, service_tree,
                        offset, losc, media_type, media_codec_type,
                        vendor_id, vendor_codec);
                losc = 0;
                break;
            case SERVICE_CATEGORY_CONTENT_PROTECTION:
                proto_tree_add_item(service_tree, hf_btavdtp_content_protection_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
                if (content_protection_type) {
                    *content_protection_type = tvb_get_letohs(tvb, offset);
                }
                proto_item_append_text(service_item, " - %s",
                    val_to_str_const(tvb_get_letohs(tvb, offset), content_protection_type_vals, "unknown"));

                offset += 2;
                losc -= 2;

                if (losc > 0) {
                    proto_tree_add_item(service_tree, hf_btavdtp_data, tvb, offset, losc, ENC_NA);
                    offset += losc;
                    losc = 0;
                }
                break;
            case SERVICE_CATEGORY_HEADER_COMPRESSION:
                proto_tree_add_item(service_tree, hf_btavdtp_header_compression_backch,   tvb, offset, 1, ENC_NA);
                proto_tree_add_item(service_tree, hf_btavdtp_header_compression_media,    tvb, offset, 1, ENC_NA);
                proto_tree_add_item(service_tree, hf_btavdtp_header_compression_recovery, tvb, offset, 1, ENC_NA);
                proto_tree_add_item(service_tree, hf_btavdtp_header_compression_rfa,      tvb, offset, 1, ENC_NA);
                offset += 1;
                losc -= 1;
                break;
            case SERVICE_CATEGORY_MULTIPLEXING:
                proto_tree_add_item(service_tree, hf_btavdtp_multiplexing_fragmentation, tvb, offset, 1, ENC_NA);
                proto_tree_add_item(service_tree, hf_btavdtp_multiplexing_rfa, tvb, offset, 1, ENC_NA);
                offset += 1;
                losc -= 1;

                if (losc >= 2) {
                    pitem = proto_tree_add_none_format(service_tree, hf_btavdtp_service_multiplexing_entry, tvb, offset, 1 + losc, "Entry: Media Transport Session");
                    ptree = proto_item_add_subtree(pitem, ett_btavdtp_service);

                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_tsid, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_entry_rfa, tvb, offset, 1, ENC_NA);
                    offset += 1;
                    losc -= 1;
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_tcid, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_entry_rfa, tvb, offset, 1, ENC_NA);
                    offset += 1;
                    losc -= 1;
                }

                if (losc >= 2) {
                    pitem = proto_tree_add_none_format(service_tree, hf_btavdtp_service_multiplexing_entry, tvb, offset, 1 + losc, "Entry: Reporting Transport Session");
                    ptree = proto_item_add_subtree(pitem, ett_btavdtp_service);

                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_tsid, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_entry_rfa, tvb, offset, 1, ENC_NA);
                    offset += 1;
                    losc -= 1;
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_tcid, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_entry_rfa, tvb, offset, 1, ENC_NA);
                    offset += 1;
                    losc  -= 1;
                }

                if (losc >= 2) {
                    pitem = proto_tree_add_none_format(service_tree, hf_btavdtp_service_multiplexing_entry, tvb, offset, 1 + losc, "Entry: Recovery Transport Session");
                    ptree = proto_item_add_subtree(pitem, ett_btavdtp_service);

                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_tsid, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_entry_rfa, tvb, offset, 1, ENC_NA);
                    offset += 1;
                    losc -= 1;
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_tcid, tvb, offset, 1, ENC_NA);
                    proto_tree_add_item(ptree, hf_btavdtp_multiplexing_entry_rfa, tvb, offset, 1, ENC_NA);
                    offset += 1;
                    losc -= 1;
                }
                break;
            default:
                proto_tree_add_item(service_tree, hf_btavdtp_data, tvb, offset, losc, ENC_NA);
                offset += losc;
                losc = 0;
        }

        if (losc > 0) {
            pitem = proto_tree_add_item(service_tree, hf_btavdtp_data, tvb, offset, losc, ENC_NA);
            offset += losc;

            expert_add_info(pinfo, pitem, &ei_btavdtp_unexpected_losc_data);
        }
    }

    return offset;
}

static gint
dissect_seid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset,
             gint seid_side, gint i_item, guint32 *sep_seid,
             guint32 interface_id, guint32 adapter_id, guint32 chandle,
             guint32 frame_number)
{
    guint32      seid;
    proto_tree  *seid_tree     = NULL;
    proto_item  *seid_item     = NULL;
    guint32      direction;

    seid = tvb_get_guint8(tvb, offset) >> 2;
    if (sep_seid) {
        *sep_seid = seid;
    }

    if (seid_side == SEID_ACP) {
        direction = pinfo->p2p_dir;
        seid_item = proto_tree_add_none_format(tree, hf_btavdtp_acp_seid_item, tvb, offset, 1,
                "ACP SEID [%u - %s %s]", seid,
                    get_sep_media_type(interface_id, adapter_id, chandle, direction, seid, frame_number),
                    get_sep_type(interface_id, adapter_id, chandle, direction, seid, frame_number));
        seid_tree = proto_item_add_subtree(seid_item, ett_btavdtp_sep);
        proto_tree_add_item(seid_tree, hf_btavdtp_acp_seid, tvb, offset, 1, ENC_NA);
        if (i_item > 0) proto_item_append_text(seid_item, " item %u", i_item);

        col_append_fstr(pinfo->cinfo, COL_INFO, " - ACP SEID [%u - %s %s]",
                seid, get_sep_media_type(interface_id, adapter_id, chandle, direction, seid, frame_number),
                get_sep_type(interface_id, adapter_id, chandle, direction, seid, frame_number));
    } else {
        direction = (pinfo->p2p_dir == P2P_DIR_SENT) ? P2P_DIR_RECV : P2P_DIR_SENT;
        seid_item = proto_tree_add_none_format(tree, hf_btavdtp_int_seid_item, tvb, offset, 1,
                "INT SEID [%u - %s %s]", seid,
                    get_sep_media_type(interface_id, adapter_id, chandle, direction, seid, frame_number),
                    get_sep_type(interface_id, adapter_id, chandle, direction, seid, frame_number));
        seid_tree = proto_item_add_subtree(seid_item, ett_btavdtp_sep);
        proto_tree_add_item(seid_tree, hf_btavdtp_int_seid, tvb, offset, 1, ENC_NA);
        if (i_item > 0) proto_item_append_text(seid_item, " item %u", i_item);

        col_append_fstr(pinfo->cinfo, COL_INFO, " - INT SEID [%u - %s %s]",
                seid, get_sep_media_type(interface_id, adapter_id, chandle, direction, seid, frame_number),
                get_sep_type(interface_id, adapter_id, chandle, direction, seid, frame_number));
    }

    proto_tree_add_item(seid_tree, hf_btavdtp_rfa_seid, tvb, offset, 1, ENC_NA);
    offset += 1;

    return offset;
}


static gint
dissect_btavdtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    proto_item       *ti;
    proto_tree       *btavdtp_tree       = NULL;
    proto_tree       *signal_tree        = NULL;
    proto_item       *signal_item        = NULL;
    btl2cap_data_t   *l2cap_data;
    gint             offset = 0;
    gint             i_sep         = 1;
    gint             packet_type   = 0;
    gint             message_type  = 0;
    gint             signal_id     = 0;
    guint            delay;
    wmem_tree_t      *subtree;
    wmem_tree_key_t  key[8];
    channels_info_t  *channels_info;
    guint32          interface_id;
    guint32          adapter_id;
    guint32          chandle;
    guint32          psm;
    guint32          direction;
    guint32          cid;
    guint32          frame_number;
    sep_entry_t      *sep;
    tvbuff_t         *next_tvb;
    guint32          seid;
    gint             codec = -1;
    gint             content_protection_type = 0;
    guint32          configuration_offset;
    guint8           configuration_length;

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "AVDTP");

    direction = pinfo->p2p_dir;
    switch (direction) {
        case P2P_DIR_SENT:
            col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
            break;

        case P2P_DIR_RECV:
            col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
            break;
        default:
            col_set_str(pinfo->cinfo, COL_INFO, "UnknownDirection ");
            goto LABEL_data;
    }

    l2cap_data = (btl2cap_data_t *) data;
    DISSECTOR_ASSERT(l2cap_data);

    interface_id = l2cap_data->interface_id;
    adapter_id = l2cap_data->adapter_id;
    chandle = l2cap_data->chandle;
    psm = l2cap_data->psm;
    cid = l2cap_data->cid;
    frame_number = pinfo->num;

    key[0].length = 1;
    key[0].key    = &interface_id;
    key[1].length = 1;
    key[1].key    = &adapter_id;
    key[2].length = 1;
    key[2].key    = &chandle;
    key[3].length = 1;
    key[3].key    = &psm;
    key[4].length = 0;
    key[4].key    = NULL;

    subtree = (wmem_tree_t *) wmem_tree_lookup32_array(channels, key);
    channels_info = (subtree) ? (channels_info_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
    if (!(channels_info &&
            ((*channels_info->adapter_disconnect_in_frame >= pinfo->num &&
            *channels_info->hci_disconnect_in_frame >= pinfo->num &&
            *channels_info->l2cap_disconnect_in_frame >= pinfo->num &&
            channels_info->disconnect_in_frame >= pinfo->num) ||
            (*channels_info->adapter_disconnect_in_frame == 0 ||
            *channels_info->hci_disconnect_in_frame == 0 ||
            *channels_info->l2cap_disconnect_in_frame == 0 ||
            channels_info->disconnect_in_frame == 0)))) {

        channels_info = (channels_info_t *) wmem_new (wmem_file_scope(), channels_info_t);
        channels_info->control_local_cid = l2cap_data->local_cid;
        channels_info->control_remote_cid = l2cap_data->remote_cid;
        channels_info->media_local_cid = BTL2CAP_UNKNOWN_CID;
        channels_info->media_remote_cid = BTL2CAP_UNKNOWN_CID;
        channels_info->disconnect_in_frame = bluetooth_max_disconnect_in_frame;
        channels_info->l2cap_disconnect_in_frame   = l2cap_data->disconnect_in_frame;
        channels_info->hci_disconnect_in_frame     = l2cap_data->hci_disconnect_in_frame;
        channels_info->adapter_disconnect_in_frame = l2cap_data->adapter_disconnect_in_frame;
        channels_info->sep = NULL;

        if (!pinfo->fd->visited || (
                *channels_info->adapter_disconnect_in_frame == 0 ||
                *channels_info->hci_disconnect_in_frame == 0 ||
                *channels_info->l2cap_disconnect_in_frame == 0 ||
                channels_info->disconnect_in_frame == 0)) {
            key[4].length = 1;
            key[4].key    = &frame_number;
            key[5].length = 0;
            key[5].key    = NULL;

            channels_info->stream_numbers = wmem_tree_new(wmem_file_scope());

            if (*channels_info->adapter_disconnect_in_frame > 0 &&
                    *channels_info->hci_disconnect_in_frame > 0 &&
                    *channels_info->l2cap_disconnect_in_frame > 0 &&
                    channels_info->disconnect_in_frame > 0) {
                wmem_tree_insert32_array(channels, key, channels_info);
            }
        } else {
            channels_info->stream_numbers = NULL;
        }
    }

    if (!(l2cap_data->local_cid == channels_info->control_local_cid &&
            l2cap_data->remote_cid == channels_info->control_remote_cid) &&
            (channels_info->media_local_cid == BTL2CAP_UNKNOWN_CID ||
            (l2cap_data->local_cid == channels_info->media_local_cid &&
            l2cap_data->remote_cid == channels_info->media_remote_cid))) {

        if (!pinfo->fd->visited && channels_info->media_local_cid == BTL2CAP_UNKNOWN_CID) {
            channels_info->media_local_cid = l2cap_data->local_cid;
            channels_info->media_remote_cid = l2cap_data->remote_cid;
        }
        /* Media Channel */

        if (!channels_info->sep) {
            ti = proto_tree_add_item(tree, proto_btavdtp, tvb, offset, -1, ENC_NA);
            btavdtp_tree = proto_item_add_subtree(ti, ett_btavdtp);

            col_append_fstr(pinfo->cinfo, COL_INFO, "Media stream on cid=0x%04x", l2cap_data->cid);
            proto_tree_add_item(btavdtp_tree, hf_btavdtp_data, tvb, offset, -1, ENC_NA);
        } else {
            col_append_fstr(pinfo->cinfo, COL_INFO, "Media stream ACP SEID [%u - %s %s]",
                    channels_info->sep->seid, get_sep_media_type(
                            interface_id, adapter_id, chandle, direction,
                            channels_info->sep->seid,
                            frame_number),
                    get_sep_type(interface_id, adapter_id, chandle, direction,
                            channels_info->sep->seid,
                            frame_number));

            if (channels_info->sep->media_type == MEDIA_TYPE_AUDIO) {
                sep_data_t                    sep_data;
                media_stream_number_value_t  *media_stream_number_value;
                media_packet_info_t          *previous_media_packet_info;
                media_packet_info_t          *current_media_packet_info;
                nstime_t                      first_abs_ts;
                gdouble                       cumulative_frame_duration;
                gdouble                       avrcp_song_position = -1.0;
                btavrcp_song_position_data_t *song_position_data;

                sep_data.codec        = channels_info->sep->codec;
                sep_data.vendor_id    = channels_info->sep->vendor_id;
                sep_data.vendor_codec = channels_info->sep->vendor_codec;
                sep_data.acp_seid     = channels_info->sep->seid;
                sep_data.int_seid     = channels_info->sep->int_seid;
                sep_data.content_protection_type = channels_info->sep->content_protection_type;
                sep_data.stream_start_in_frame   = 0;
                sep_data.stream_end_in_frame     = 0;
                sep_data.configuration_length    = channels_info->sep->configuration_length;
                sep_data.configuration           = channels_info->sep->configuration;

                media_stream_number_value = (media_stream_number_value_t *) wmem_tree_lookup32_le(channels_info->stream_numbers, frame_number - 1);
                if (media_stream_number_value) {
                    sep_data.stream_number         = media_stream_number_value->stream_number;
                    if (media_stream_number_value->stream_start_in_frame == 0)
                        media_stream_number_value->stream_start_in_frame = pinfo->num;

                    if (!pinfo->fd->visited)
                        media_stream_number_value->stream_end_in_frame = pinfo->num;

                    sep_data.stream_start_in_frame = media_stream_number_value->stream_start_in_frame;
                    sep_data.stream_end_in_frame   = media_stream_number_value->stream_end_in_frame;
                } else {
                    sep_data.stream_number = 1;
                }

                key[0].length = 1;
                key[0].key    = &interface_id;
                key[1].length = 1;
                key[1].key    = &adapter_id;
                key[3].length = 1;
                key[3].key    = &cid;
                key[4].length = 1;
                key[4].key    = &direction;
                key[5].length = 0;
                key[5].key    = NULL;

                key[2].length = 0;
                key[2].key    = NULL;

                subtree = (wmem_tree_t *) wmem_tree_lookup32_array(btavrcp_song_positions, key);
                song_position_data = (subtree) ? (btavrcp_song_position_data_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
                if (song_position_data && (song_position_data->used_in_frame == 0 ||
                        song_position_data->used_in_frame == frame_number)) {
                    avrcp_song_position = song_position_data->song_position;
                    if (!pinfo->fd->visited)
                        song_position_data->used_in_frame = frame_number;
                }

                key[2].length = 1;
                key[2].key    = &chandle;

                subtree = (wmem_tree_t *) wmem_tree_lookup32_array(media_packet_times, key);
                previous_media_packet_info = (subtree) ? (media_packet_info_t *) wmem_tree_lookup32_le(subtree, frame_number - 1) : NULL;
                if (previous_media_packet_info && previous_media_packet_info->stream_number == sep_data.stream_number ) {
                    sep_data.previous_media_packet_info = previous_media_packet_info;
                    first_abs_ts = previous_media_packet_info->first_abs_ts;
                    cumulative_frame_duration = previous_media_packet_info->cumulative_frame_duration;
                    if (avrcp_song_position == -1.0)
                        avrcp_song_position = previous_media_packet_info->avrcp_song_position;
                    else
                        previous_media_packet_info->avrcp_song_position = avrcp_song_position;
                } else {
                    if (avrcp_song_position == -1.0)
                        avrcp_song_position = 0.0;
                    first_abs_ts = pinfo->abs_ts;
                    cumulative_frame_duration = 0.0;
                    sep_data.previous_media_packet_info = (media_packet_info_t *) wmem_new(wmem_epan_scope(), media_packet_info_t);
                    sep_data.previous_media_packet_info->abs_ts = pinfo->abs_ts;
                    sep_data.previous_media_packet_info->first_abs_ts = first_abs_ts;
                    sep_data.previous_media_packet_info->cumulative_frame_duration = cumulative_frame_duration;
                    sep_data.previous_media_packet_info->avrcp_song_position = avrcp_song_position;
                    sep_data.previous_media_packet_info->stream_number = sep_data.stream_number;
                }

                if (!pinfo->fd->visited) {
                    key[5].length = 1;
                    key[5].key    = &frame_number;
                    key[6].length = 0;
                    key[6].key    = NULL;

                    if (avrcp_song_position == -1.0)
                        avrcp_song_position = 0.0;

                    current_media_packet_info = wmem_new(wmem_file_scope(), media_packet_info_t);
                    current_media_packet_info->abs_ts = pinfo->abs_ts;
                    current_media_packet_info->first_abs_ts = first_abs_ts;
                    current_media_packet_info->cumulative_frame_duration = cumulative_frame_duration;
                    current_media_packet_info->avrcp_song_position = avrcp_song_position;
                    current_media_packet_info->stream_number = sep_data.stream_number;

                    wmem_tree_insert32_array(media_packet_times, key, current_media_packet_info);
                }

                key[5].length = 0;
                key[5].key    = NULL;

                subtree = (wmem_tree_t *) wmem_tree_lookup32_array(media_packet_times, key);
                current_media_packet_info = (subtree) ? (media_packet_info_t *) wmem_tree_lookup32(subtree, frame_number) : NULL;
                if (current_media_packet_info)
                    sep_data.current_media_packet_info = current_media_packet_info;
                else
                    sep_data.current_media_packet_info = NULL;

                next_tvb = tvb_new_subset_remaining(tvb, offset);
                call_dissector_with_data(bta2dp_handle, next_tvb, pinfo, tree, &sep_data);
            } else if (channels_info->sep->media_type == MEDIA_TYPE_VIDEO) {
                sep_data_t                    sep_data;
                media_stream_number_value_t  *media_stream_number_value;

                sep_data.codec        = channels_info->sep->codec;
                sep_data.vendor_id    = channels_info->sep->vendor_id;
                sep_data.vendor_codec = channels_info->sep->vendor_codec;
                sep_data.acp_seid     = channels_info->sep->seid;
                sep_data.int_seid     = channels_info->sep->int_seid;
                sep_data.content_protection_type = channels_info->sep->content_protection_type;
                sep_data.stream_start_in_frame   = 0;
                sep_data.stream_end_in_frame     = 0;
                sep_data.configuration_length    = channels_info->sep->configuration_length;
                sep_data.configuration           = channels_info->sep->configuration;

                media_stream_number_value = (media_stream_number_value_t *) wmem_tree_lookup32_le(channels_info->stream_numbers, frame_number - 1);
                if (media_stream_number_value) {
                    sep_data.stream_number = media_stream_number_value->stream_number;
                } else {
                    sep_data.stream_number = 1;
                }

                next_tvb = tvb_new_subset_remaining(tvb, offset);
                call_dissector_with_data(btvdp_handle, next_tvb, pinfo, tree, &sep_data);
            } else {
                ti = proto_tree_add_item(tree, proto_btavdtp, tvb, offset, -1, ENC_NA);
                btavdtp_tree = proto_item_add_subtree(ti, ett_btavdtp);

                col_append_fstr(pinfo->cinfo, COL_INFO, "Media stream on cid=0x%04x", l2cap_data->cid);
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_data, tvb, offset, -1, ENC_NA);
            }
        }

        return tvb_reported_length(tvb);
    } else if (!(l2cap_data->local_cid == channels_info->control_local_cid &&
            l2cap_data->remote_cid == channels_info->control_remote_cid)) {
        /* Unknown Stream Channel */
        ti = proto_tree_add_item(tree, proto_btavdtp, tvb, offset, -1, ENC_NA);
        btavdtp_tree = proto_item_add_subtree(ti, ett_btavdtp);

        col_append_fstr(pinfo->cinfo, COL_INFO, "Unknown channel stream on cid=0x%04x", l2cap_data->cid);
        proto_tree_add_item(btavdtp_tree, hf_btavdtp_data, tvb, offset, -1, ENC_NA);
        return tvb_reported_length(tvb);
    }

    /* Signaling Channel */
    ti = proto_tree_add_item(tree, proto_btavdtp, tvb, offset, -1, ENC_NA);
    btavdtp_tree = proto_item_add_subtree(ti, ett_btavdtp);

    /* AVDTP signaling*/
    message_type = (tvb_get_guint8(tvb, offset) & AVDTP_MESSAGE_TYPE_MASK);
    packet_type = (tvb_get_guint8(tvb, offset) & AVDTP_PACKET_TYPE_MASK) >> 2;

    signal_item = proto_tree_add_item(btavdtp_tree, hf_btavdtp_signal, tvb, offset,
            (packet_type == PACKET_TYPE_START) ? 3 : 2, ENC_NA);
    signal_tree = proto_item_add_subtree(signal_item, ett_btavdtp_sep);

    proto_tree_add_item(signal_tree, hf_btavdtp_transaction, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(signal_tree, hf_btavdtp_packet_type, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(signal_tree, hf_btavdtp_message_type, tvb, offset, 1, ENC_NA);

    if (packet_type == PACKET_TYPE_START) {
        offset += 1;
        proto_tree_add_item(signal_tree, hf_btavdtp_number_of_signal_packets, tvb, offset, 1, ENC_NA);
    }

    if (packet_type == PACKET_TYPE_CONTINUE || packet_type == PACKET_TYPE_END) goto LABEL_data;

    offset += 1;
    proto_tree_add_item(signal_tree, hf_btavdtp_rfa0,         tvb, offset, 1, ENC_NA);
    proto_tree_add_item(signal_tree, hf_btavdtp_signal_id,    tvb, offset, 1, ENC_NA);

    signal_id   = tvb_get_guint8(tvb, offset) & AVDTP_SIGNAL_ID_MASK;
    proto_item_append_text(signal_item, ": %s (%s)",
            val_to_str_const(signal_id, signal_id_vals, "Unknown signal"),
            val_to_str_const(message_type, message_type_vals, "Unknown message type"));

    col_append_fstr(pinfo->cinfo, COL_INFO, "%s - %s",
                    val_to_str_const(message_type, message_type_vals, "Unknown message type"),
                    val_to_str_const(signal_id, signal_id_vals, "Unknown signal"));

    offset += 1;
    if (message_type != MESSAGE_TYPE_GENERAL_REJECT) switch (signal_id) {
        case SIGNAL_ID_DISCOVER:
            if (message_type == MESSAGE_TYPE_COMMAND) break;
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            offset = dissect_sep(tvb, pinfo, btavdtp_tree, offset,
                    interface_id, adapter_id, chandle);
            break;
        case SIGNAL_ID_GET_CAPABILITIES:
        case SIGNAL_ID_GET_ALL_CAPABILITIES:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL, interface_id,
                        adapter_id, chandle, frame_number);
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            offset = dissect_capabilities(tvb, pinfo, btavdtp_tree, offset, NULL, NULL, NULL, NULL, NULL, NULL);
            break;
        case SIGNAL_ID_SET_CONFIGURATION:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                guint32  int_seid;
                guint32  vendor_id;
                guint16  vendor_codec;
                guint32  reverse_direction;

                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, &seid, interface_id,
                        adapter_id, chandle, frame_number);
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_INT, 0, &int_seid, interface_id,
                        adapter_id, chandle, frame_number);
                offset = dissect_capabilities(tvb, pinfo, btavdtp_tree, offset,
                        &codec, &content_protection_type, &vendor_id,
                        &vendor_codec, &configuration_offset, &configuration_length);

                if (!pinfo->fd->visited) {
                    key[0].length = 1;
                    key[0].key    = &interface_id;
                    key[1].length = 1;
                    key[1].key    = &adapter_id;
                    key[2].length = 1;
                    key[2].key    = &chandle;
                    key[3].length = 1;
                    key[3].key    = &direction;
                    key[4].length = 1;
                    key[4].key    = &seid;
                    key[5].length = 0;
                    key[5].key    = NULL;

                    subtree = (wmem_tree_t *) wmem_tree_lookup32_array(sep_list, key);
                    sep = (subtree) ? (sep_entry_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
                    if (sep) {
                        sep->codec = codec;
                        sep->vendor_id = vendor_id;
                        sep->vendor_codec = vendor_codec;
                        sep->content_protection_type = content_protection_type;
                        sep->int_seid = int_seid;
                        if (configuration_length > 0) {
                            sep->configuration_length = configuration_length;
                            sep->configuration = (guint8 *) tvb_memdup(wmem_file_scope(),
                                    tvb, configuration_offset, configuration_length);
                        }

                        if (direction == P2P_DIR_SENT)
                            reverse_direction = P2P_DIR_RECV;
                        else if (direction == P2P_DIR_RECV)
                            reverse_direction = P2P_DIR_SENT;
                        else
                            reverse_direction = P2P_DIR_UNKNOWN;

                        key[3].length = 1;
                        key[3].key    = &reverse_direction;
                        key[4].length = 1;
                        key[4].key    = &int_seid;
                        key[5].length = 1;
                        key[5].key    = &frame_number;
                        key[6].length = 0;
                        key[6].key    = NULL;

                        wmem_tree_insert32_array(sep_list, key, sep);
                    }

                }

                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_service_category, tvb, offset, 1, ENC_NA);
                offset += 1;

                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            break;
        case SIGNAL_ID_GET_CONFIGURATION:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL, interface_id,
                        adapter_id, chandle, frame_number);
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            offset = dissect_capabilities(tvb, pinfo, btavdtp_tree, offset, NULL, NULL, NULL, NULL, NULL, NULL);
            break;
        case SIGNAL_ID_RECONFIGURE:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                guint32  vendor_id;
                guint16  vendor_codec;

                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, &seid, interface_id,
                        adapter_id, chandle, frame_number);
                offset = dissect_capabilities(tvb, pinfo, btavdtp_tree, offset,
                        &codec, &content_protection_type, &vendor_id,
                        &vendor_codec, &configuration_offset, &configuration_length);

                if (!pinfo->fd->visited) {
                    key[0].length = 1;
                    key[0].key    = &interface_id;
                    key[1].length = 1;
                    key[1].key    = &adapter_id;
                    key[2].length = 1;
                    key[2].key    = &chandle;
                    key[3].length = 1;
                    key[3].key    = &direction;
                    key[4].length = 1;
                    key[4].key    = &seid;
                    key[5].length = 0;
                    key[5].key    = NULL;

                    subtree = (wmem_tree_t *) wmem_tree_lookup32_array(sep_list, key);
                    sep = (subtree) ? (sep_entry_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
                    if (sep) {
                        sep->codec = codec;
                        sep->vendor_id = vendor_id;
                        sep->vendor_codec = vendor_codec;
                        sep->content_protection_type = content_protection_type;
                        if (configuration_length > 0) {
                            sep->configuration_length = configuration_length;
                            sep->configuration = (guint8 *) tvb_memdup(wmem_file_scope(),
                                    tvb, configuration_offset, configuration_length);
                        }
                    }
                }

                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_service_category, tvb, offset, 1, ENC_NA);
                offset += 1;

                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            break;
        case SIGNAL_ID_OPEN:
             if (message_type == MESSAGE_TYPE_COMMAND) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, &seid, interface_id,
                        adapter_id, chandle, frame_number);

                if (!pinfo->fd->visited) {
                    key[0].length = 1;
                    key[0].key    = &interface_id;
                    key[1].length = 1;
                    key[1].key    = &adapter_id;
                    key[2].length = 1;
                    key[2].key    = &chandle;
                    key[3].length = 1;
                    key[3].key    = &direction;
                    key[4].length = 1;
                    key[4].key    = &seid;
                    key[5].length = 0;
                    key[5].key    = NULL;

                    subtree = (wmem_tree_t *) wmem_tree_lookup32_array(sep_list, key);
                    sep = (subtree) ? (sep_entry_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
                    if (sep) {
                        sep->state = SEP_STATE_OPEN;

                        key[3].length = 1;
                        key[3].key    = &frame_number;
                        key[4].length = 0;
                        key[4].key    = NULL;

                        wmem_tree_insert32_array(sep_open, key, sep);
                    }
                }
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            if (message_type == MESSAGE_TYPE_ACCEPT && !pinfo->fd->visited) {

                key[0].length = 1;
                key[0].key    = &interface_id;
                key[1].length = 1;
                key[1].key    = &adapter_id;
                key[2].length = 1;
                key[2].key    = &chandle;
                key[3].length = 0;
                key[3].key    = NULL;

                subtree = (wmem_tree_t *) wmem_tree_lookup32_array(sep_open, key);
                sep = (subtree) ? (sep_entry_t *) wmem_tree_lookup32_le(subtree, frame_number) : NULL;
                if (sep && sep->state == SEP_STATE_OPEN) {
                    sep->state = SEP_STATE_IN_USE;
                    channels_info->sep = sep;
                }
            }
            break;
        case SIGNAL_ID_START:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                i_sep = 1;
                while (tvb_reported_length_remaining(tvb, offset) > 0) {
                    offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                            SEID_ACP, i_sep, NULL,
                            interface_id, adapter_id, chandle, frame_number);
                    i_sep += 1;
                }
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL,
                        interface_id, adapter_id, chandle, frame_number);
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }

            if (message_type == MESSAGE_TYPE_ACCEPT && !pinfo->fd->visited) {
                media_stream_number_value_t  *media_stream_number_value;
                guint32                       stream_number = 0;

                media_stream_number_value = (media_stream_number_value_t *) wmem_tree_lookup32_le(channels_info->stream_numbers, frame_number - 1);
#if RTP_PLAYER_WORKAROUND == TRUE
                {
                    media_stream_number_value_t  *file_scope_stream_number_value;

                    if (media_stream_number_value) {
                        stream_number = media_stream_number_value->stream_number;
                    } else {
                        file_scope_stream_number_value = (media_stream_number_value_t *) wmem_tree_lookup32_le(file_scope_stream_number, frame_number - 1);
                        if (file_scope_stream_number_value)
                            stream_number = file_scope_stream_number_value->stream_number + 1;
                        else
                            stream_number = 0;
                    }

                    file_scope_stream_number_value = wmem_new(wmem_file_scope(), media_stream_number_value_t);
                    file_scope_stream_number_value->stream_number = stream_number;
                    wmem_tree_insert32(file_scope_stream_number, frame_number, file_scope_stream_number_value);
                }
#else
                if (media_stream_number_value)
                    stream_number = media_stream_number_value->stream_number;
                else
                    stream_number = 0;
#endif

                media_stream_number_value = wmem_new(wmem_file_scope(), media_stream_number_value_t);
                media_stream_number_value->stream_number = stream_number + 1;
                media_stream_number_value->stream_start_in_frame = 0;
                media_stream_number_value->stream_end_in_frame = 0;

                wmem_tree_insert32(channels_info->stream_numbers, frame_number, media_stream_number_value);
            }
            break;
        case SIGNAL_ID_CLOSE:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL, interface_id,
                        adapter_id, chandle, frame_number);
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            if (!pinfo->fd->visited && message_type == MESSAGE_TYPE_ACCEPT &&
                    channels_info->disconnect_in_frame > pinfo->num) {
                channels_info->disconnect_in_frame = pinfo->num;
            }
            break;
        case SIGNAL_ID_SUSPEND:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                i_sep = 1;
                while (tvb_reported_length_remaining(tvb, offset) > 0) {
                    offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                            SEID_ACP, i_sep, NULL,
                            interface_id, adapter_id, chandle, frame_number);
                    i_sep += 1;
                }
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL, interface_id,
                        adapter_id, chandle, frame_number);
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            break;
        case SIGNAL_ID_ABORT:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL, interface_id,
                        adapter_id, chandle, frame_number);
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            if (!pinfo->fd->visited && message_type == MESSAGE_TYPE_ACCEPT &&
                    channels_info->disconnect_in_frame > pinfo->num) {
                channels_info->disconnect_in_frame = pinfo->num;
            }
            break;
        case SIGNAL_ID_SECURITY_CONTROL:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL, interface_id,
                        adapter_id, chandle, frame_number);
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_data, tvb, offset, -1, ENC_NA);
                offset += tvb_reported_length_remaining(tvb, offset);
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }

            proto_tree_add_item(btavdtp_tree, hf_btavdtp_data, tvb, offset, -1, ENC_NA);
            offset += tvb_reported_length_remaining(tvb, offset);
            break;
        case SIGNAL_ID_DELAY_REPORT:
            if (message_type == MESSAGE_TYPE_COMMAND) {
                proto_item  *pitem;
                delay = tvb_get_ntohs(tvb, offset + 1);
                col_append_fstr(pinfo->cinfo, COL_INFO, "(%u.%u ms)", delay/10, delay%10);
                offset = dissect_seid(tvb, pinfo, btavdtp_tree, offset,
                        SEID_ACP, 0, NULL,
                        interface_id, adapter_id, chandle, frame_number);
                pitem = proto_tree_add_item(btavdtp_tree, hf_btavdtp_delay, tvb, offset, 2, ENC_BIG_ENDIAN);
                proto_item_append_text(pitem, " (1/10 ms)");
                offset += 2;
                break;
            }
            if (message_type == MESSAGE_TYPE_REJECT) {
                proto_tree_add_item(btavdtp_tree, hf_btavdtp_error_code, tvb, offset, 1, ENC_NA);
                offset += 1;
                break;
            }
            break;
    }

    LABEL_data:

    if (tvb_reported_length_remaining(tvb, offset) > 0) {
        proto_tree_add_item(btavdtp_tree, hf_btavdtp_data, tvb, offset, -1, ENC_NA);
    }

    return offset;
}


void
proto_register_btavdtp(void)
{
    module_t *module;

    static hf_register_info hf[] = {
        { &hf_btavdtp_signal,
            { "Signal",                   "btavdtp.signal",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_message_type,
            { "Message Type",                   "btavdtp.message_type",
            FT_UINT8, BASE_HEX, VALS(message_type_vals), AVDTP_MESSAGE_TYPE_MASK,
            NULL, HFILL }
        },
        { &hf_btavdtp_packet_type,
            { "Packet Type",                    "btavdtp.packet_type",
            FT_UINT8, BASE_HEX, VALS(packet_type_vals), AVDTP_PACKET_TYPE_MASK,
            NULL, HFILL }
        },
        { &hf_btavdtp_transaction,
            { "Transaction",                    "btavdtp.transaction",
            FT_UINT8, BASE_HEX, NULL, AVDTP_TRANSACTION_MASK,
            NULL, HFILL }
        },
        { &hf_btavdtp_signal_id,
            { "Signal",                         "btavdtp.signal_id",
            FT_UINT8, BASE_HEX, VALS(signal_id_vals), AVDTP_SIGNAL_ID_MASK,
            NULL, HFILL }
        },
        { &hf_btavdtp_rfa0,
            { "RFA",                            "btavdtp.rfa0",
            FT_UINT8, BASE_HEX, NULL, AVDTP_RFA0_MASK,
            NULL, HFILL }
        },
        { &hf_btavdtp_number_of_signal_packets,
            { "Number of signal packets",       "btavdtp.num_signal_packets",
            FT_UINT8, BASE_DEC, NULL, 0,
            NULL, HFILL }
        },
        { &hf_btavdtp_error_code,
            { "Error Code",                     "btavdtp.error_code",
            FT_UINT8, BASE_HEX, VALS(error_code_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_sep_seid,
            { "SEID",                           "btavdtp.sep_seid",
            FT_UINT8, BASE_DEC, NULL, 0xFC,
            NULL, HFILL }
        },
        { &hf_btavdtp_sep_inuse,
            { "In Use",                         "btavdtp.sep_inuse",
            FT_UINT8, BASE_HEX, VALS(true_false), 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_sep_rfa0,
            { "RFA0",                           "btavdtp.sep_rfa0",
            FT_UINT8, BASE_HEX, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_sep_media_type,
            { "Media Type",                     "btavdtp.sep_media_type",
            FT_UINT8, BASE_HEX, VALS(media_type_vals), 0xF0,
            NULL, HFILL }
        },
        { &hf_btavdtp_sep_type,
            { "Type",                           "btavdtp.sep_type",
            FT_UINT8, BASE_HEX, VALS(sep_type_vals), 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_sep_rfa1,
            { "RFA1",                           "btavdtp.sep_rfa1",
            FT_UINT8, BASE_HEX, NULL, 0x07,
            NULL, HFILL }
        },
        { &hf_btavdtp_acp_sep,
            { "ACP SEP",                        "btavdtp.acp_sep",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_acp_seid_item,
            { "ACP SEID",                       "btavdtp.acp_seid_item",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_int_seid_item,
            { "INT SEID",                       "btavdtp.int_seid_item",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_acp_seid,
            { "ACP SEID",                       "btavdtp.acp_seid",
            FT_UINT8, BASE_DEC, NULL, 0xFC,
            NULL, HFILL }
        },
        { &hf_btavdtp_int_seid,
            { "INT SEID",                       "btavdtp.int_seid",
            FT_UINT8, BASE_DEC, NULL, 0xFC,
            NULL, HFILL }
        },
        { &hf_btavdtp_rfa_seid,
            { "RFA",                            "btavdtp.rfa_seid",
            FT_UINT8, BASE_HEX, NULL, 0x03,
            NULL, HFILL }
        },
        { &hf_btavdtp_service_category,
            { "Service Category",               "btavdtp.service_category",
            FT_UINT8, BASE_HEX, VALS(service_category_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_length_of_service_category,
            { "Length of Service Category",     "btavdtp.length_of_service_category",
            FT_UINT8, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_delay,
            { "Delay",                          "btavdtp.delay",
            FT_UINT16, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_recovery_type,
            { "Service Category",               "btavdtp.recovery_type",
            FT_UINT8, BASE_HEX, VALS(recovery_type_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_maximum_recovery_window_size,
            { "Service Category",               "btavdtp.maximum_recovery_window_size",
            FT_UINT8, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_maximum_number_of_media_packet_in_parity_code,
            { "Service Category",               "btavdtp.maximum_number_of_media_packet_in_parity_code",
            FT_UINT8, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_multiplexing_fragmentation,
            { "Fragmentation",                  "btavdtp.multiplexing_fragmentation",
            FT_UINT8, BASE_HEX, VALS(true_false), 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_multiplexing_rfa,
            { "RFA",                            "btavdtp.multiplexing_rfa",
            FT_UINT8, BASE_HEX, NULL, 0x7F,
            NULL, HFILL }
        },
        { &hf_btavdtp_multiplexing_tsid,
            { "TSID",                           "btavdtp.multiplexing_tsid",
            FT_UINT8, BASE_HEX, VALS(multiplexing_tsid_vals), 0xF8,
            NULL, HFILL }
        },
        { &hf_btavdtp_multiplexing_tcid,
            { "TCID",                           "btavdtp.multiplexing_tcid",
            FT_UINT8, BASE_HEX, VALS(multiplexing_tcid_vals), 0xF8,
            NULL, HFILL }
        },
        { &hf_btavdtp_multiplexing_entry_rfa,
            { "RFA",                            "btavdtp.multiplexing_entry_rfa",
            FT_UINT8, BASE_HEX, NULL, 0x07,
            NULL, HFILL }
        },
        { &hf_btavdtp_header_compression_backch,
            { "BackCh",                         "btavdtp.header_compression_backch",
            FT_UINT8, BASE_HEX, VALS(true_false), 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_header_compression_media,
            { "Media",                          "btavdtp.header_compression_media",
            FT_UINT8, BASE_HEX, VALS(true_false), 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_header_compression_recovery,
            { "Recovery",                       "btavdtp.header_compression_recovery",
            FT_UINT8, BASE_HEX, VALS(true_false), 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_header_compression_rfa,
            { "RFA",                            "btavdtp.header_compression_rfa",
            FT_UINT8, BASE_HEX, NULL, 0x1f,
            NULL, HFILL }
        },
        { &hf_btavdtp_content_protection_type,
            { "Type",                           "btavdtp.content_protection_type",
            FT_UINT16, BASE_HEX, VALS(content_protection_type_vals), 0x0,
            NULL, HFILL }
        },
        { &hf_btavdtp_media_codec_media_type,
            { "Media Type",                     "btavdtp.media_codec_media_type",
            FT_UINT8, BASE_HEX, VALS(media_type_vals), 0xF0,
            NULL, HFILL }
        },
        { &hf_btavdtp_media_codec_rfa,
            { "RFA",                            "btavdtp.media_codec_rfa",
            FT_UINT8, BASE_HEX, NULL, 0x0F,
            NULL, HFILL }
        },
        { &hf_btavdtp_media_codec_audio_type,
            { "Media Codec Audio Type",         "btavdtp.media_codec_audio_type",
            FT_UINT8, BASE_HEX, VALS(media_codec_audio_type_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_media_codec_video_type,
            { "Media Codec Video Type",         "btavdtp.media_codec_video_type",
            FT_UINT8, BASE_HEX, VALS(media_codec_video_type_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_media_codec_unknown_type,
            { "Media Codec Unknown Type",       "btavdtp.media_codec_unknown_type",
            FT_UINT8, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_sampling_frequency_16000,
            { "Sampling Frequency 16000 Hz",    "btavdtp.codec.sbc.sampling_frequency.16000",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_sampling_frequency_32000,
            { "Sampling Frequency 32000 Hz",    "btavdtp.codec.sbc.sampling_frequency.32000",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_sampling_frequency_44100,
            { "Sampling Frequency 44100 Hz",    "btavdtp.codec.sbc.sampling_frequency.44100",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_sampling_frequency_48000,
            { "Sampling Frequency 48000 Hz",    "btavdtp.codec.sbc.sampling_frequency.48000",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_channel_mode_mono,
            { "Channel Mode Mono",              "btavdtp.codec.sbc.channel_mode.mono",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_channel_mode_dual_channel,
            { "Channel Mode Dual Channel",      "btavdtp.codec.sbc.channel_mode.dual_channel",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_channel_mode_stereo,
            { "Channel Mode Stereo",            "btavdtp.codec.sbc.channel_mode.stereo",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_channel_mode_joint_stereo,
            { "Channel Mode Joint Stereo",      "btavdtp.codec.sbc.channel_mode.joint_stereo",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_block_4,
            { "Block Length 4",                 "btavdtp.codec.sbc.block.4",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_block_8,
            { "Block Length 8",                 "btavdtp.codec.sbc.block.8",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_block_12,
            { "Block Length 12",                "btavdtp.codec.sbc.block.12",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_block_16,
            { "Block Length 16",                "btavdtp.codec.sbc.block.16",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_subbands_4,
            { "Subbands 4",                     "btavdtp.codec.sbc.subbands.4",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_subbands_8,
            { "Subbands 8",                     "btavdtp.codec.sbc.subbands.8",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_allocation_method_snr,
            { "Allocation Method SNR",          "btavdtp.codec.sbc.allocation_method.snr",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_allocation_method_loudness,
            { "Allocation Method Loudness",     "btavdtp.codec.sbc.allocation_method.loudness",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_min_bitpool,
            { "Minimum Bitpool",                "btavdtp.codec.sbc.minimum_bitpool",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_sbc_max_bitpool,
            { "Maximum Bitpool",                "btavdtp.codec.sbc.maximum_bitpool",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_layer_1,
            { "MP1",                            "btavdtp.codec.mpeg12.layer_1",
            FT_BOOLEAN, 8, NULL, 0x80,
            "MPEG Layer 1", HFILL }
        },
        { &hf_btavdtp_mpeg12_layer_2,
            { "MP2",                            "btavdtp.codec.mpeg12.layer_2",
            FT_BOOLEAN, 8, NULL, 0x40,
            "MPEG Layer 2", HFILL }
        },
        { &hf_btavdtp_mpeg12_layer_3,
            { "MP3",                            "btavdtp.codec.mpeg12.layer_3",
            FT_BOOLEAN, 8, NULL, 0x20,
            "MPEG Layer 3", HFILL }
        },
        { &hf_btavdtp_mpeg12_crc_protection,
            { "CRC Protection",                 "btavdtp.codec.mpeg12.crc_protection",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_channel_mode_mono,
            { "Channel Mode Mono",              "btavdtp.codec.mpeg12.channel_mode.mono",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_channel_mode_dual_channel,
            { "Channel Mode Dual Channel",      "btavdtp.codec.mpeg12.channel_mode.dual_channel",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_channel_mode_stereo,
            { "Channel Mode Stereo",            "btavdtp.codec.mpeg12.channel_mode.stereo",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_channel_mode_joint_stereo,
            { "Channel Mode Joint Stereo",      "btavdtp.codec.mpeg12.channel_mode.joint_stereo",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_rfa,
            { "RFA",                            "btavdtp.codec.mpeg12.rfa",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_mpf_2,
            { "Media Payload Format 2",         "btavdtp.codec.mpeg12.mpf_2",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_sampling_frequency_16000,
            { "Sampling Frequency 16000 Hz",    "btavdtp.codec.sbc.sampling_frequency.16000",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_sampling_frequency_22050,
            { "Sampling Frequency 22050 Hz",    "btavdtp.codec.sbc.sampling_frequency.22050",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_sampling_frequency_24000,
            { "Sampling Frequency 24000 Hz",    "btavdtp.codec.sbc.sampling_frequency.24000",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_sampling_frequency_32000,
            { "Sampling Frequency 32000 Hz",    "btavdtp.codec.sbc.sampling_frequency.32000",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_sampling_frequency_44100,
            { "Sampling Frequency 44100 Hz",    "btavdtp.codec.sbc.sampling_frequency.44100",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_sampling_frequency_48000,
            { "Sampling Frequency 48000 Hz",    "btavdtp.codec.sbc.sampling_frequency.48000",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_vbr_supported,
            { "VBR Supported",                  "btavdtp.codec.mpeg12.vbr",
            FT_BOOLEAN, 16, NULL, 0x8000,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg12_bit_rate,
            { "Bit Rate",                       "btavdtp.codec.mpeg12.bit_rate",
            FT_UINT16, BASE_HEX, NULL, 0x7FFF,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_object_type_mpeg2_aac_lc,
            { "MPEG2 AAC LC",                   "btavdtp.codec.mpeg24.object_type.mpeg2_aac_lc",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_object_type_mpeg4_aac_lc,
            { "MPEG4 AAC LC",                   "btavdtp.codec.mpeg24.object_type.mpeg4_aac_lc",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_object_type_mpeg4_aac_ltp,
            { "MPEG4 AAC LTP",                  "btavdtp.codec.mpeg24.object_type.mpeg4_aac_ltp",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_object_type_mpeg4_aac_scalable,
            { "MPEG4 AAC Scalable",             "btavdtp.codec.mpeg24.object_type.mpeg4_aac_scalable",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_object_type_rfa,
            { "RFA",                            "btavdtp.codec.mpeg24.object_type.rfa",
            FT_UINT8, BASE_HEX, NULL, 0x0F,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_8000,
            { "Sampling Frequency 8000 Hz",     "btavdtp.codec.mpeg24.sampling_frequency.8000",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_11025,
            { "Sampling Frequency 11025 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.11025",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_12000,
            { "Sampling Frequency 12000 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.12000",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_16000,
            { "Sampling Frequency 16000 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.16000",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_22050,
            { "Sampling Frequency 22050 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.22050",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_24000,
            { "Sampling Frequency 24000 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.24000",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_32000,
            { "Sampling Frequency 32000 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.32000",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_44100,
            { "Sampling Frequency 44100 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.44100",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_48000,
            { "Sampling Frequency 48000 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.48000",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_64000,
            { "Sampling Frequency 64000 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.64000",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_88200,
            { "Sampling Frequency 88200 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.88200",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_sampling_frequency_96000,
            { "Sampling Frequency 96000 Hz",    "btavdtp.codec.mpeg24.sampling_frequency.96000",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_channels_1,
            { "Channels 1",                     "btavdtp.codec.mpeg24.channels.1",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_channels_2,
            { "Channels 2",                     "btavdtp.codec.mpeg24.channels.2",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_rfa,
            { "RFA",                            "btavdtp.codec.mpeg24.rfa",
            FT_UINT8, BASE_HEX, NULL, 0x03,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_vbr_supported,
            { "VBR Supported",                  "btavdtp.codec.mpeg24.vbr",
            FT_BOOLEAN, 24, NULL, 0x800000,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg24_bit_rate,
            { "Bit Rate",                       "btavdtp.codec.mpeg24.bit_rate",
            FT_UINT24, BASE_HEX, NULL, 0x7FFFFF,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_version,
            { "Version",                        "btavdtp.codec.atrac.version",
            FT_UINT8, BASE_DEC, NULL, 0xE0,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_channel_mode_single_channel,
            { "Channel Mode Single Channel",    "btavdtp.codec.atrac.channel_mode.single_channel",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_channel_mode_dual_channel,
            { "Channel Mode Dual Channel",      "btavdtp.codec.atrac.channel_mode.dual_channel",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_channel_mode_joint_stereo,
            { "Channel Mode Joint Stereo",      "btavdtp.codec.atrac.channel_mode.joint_stereo",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_rfa1,
            { "RFA",                            "btavdtp.codec.atrac.rfa1",
            FT_UINT8, BASE_HEX, NULL, 0x03,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_rfa2,
            { "RFA",                            "btavdtp.codec.atrac.rfa2",
            FT_UINT24, BASE_HEX, NULL, 0xC00000,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_sampling_frequency_44100,
            { "Sampling Frequency 44100 Hz",    "btavdtp.codec.sbc.sampling_frequency.44100",
            FT_BOOLEAN, 24, NULL, 0x200000,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_sampling_frequency_48000,
            { "Sampling Frequency 48000 Hz",    "btavdtp.codec.sbc.sampling_frequency.48000",
            FT_BOOLEAN, 24, NULL, 0x100000,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_vbr_supported,
            { "VBR Supported",                  "btavdtp.codec.atrac.vbr",
            FT_BOOLEAN, 24, NULL, 0x080000,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_bit_rate,
            { "Bit Rate",                       "btavdtp.codec.atrac.bit_rate",
            FT_UINT24, BASE_HEX, NULL, 0x07FFFF,
            NULL, HFILL }
        },
        { &hf_btavdtp_atrac_maximum_sul,
            { "Maximum SUL",                    "btavdtp.codec.atrac.maximum_sul",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            "Sound Unit Length (SUL) is one of the parameters that determine bit rate of the audio stream.", HFILL }
        },
        { &hf_btavdtp_atrac_rfa3,
            { "RFA",                            "btavdtp.codec.atrac.rfa3",
            FT_UINT8, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_h263_level_10,
            { "H264 Level 10",                  "btavdtp.codec.h264.level.10",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_h263_level_20,
            { "H264 Level 20",                  "btavdtp.codec.h264.level.20",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_h263_level_30,
            { "H264 Level 30",                  "btavdtp.codec.h264.level.30",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_h263_level_rfa,
            { "H264 Level RFA",                 "btavdtp.codec.h264.level.rfa",
            FT_UINT8, BASE_HEX, NULL, 0x1F,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg4_level_0,
            { "MPEG Level 0",                   "btavdtp.codec.mpeg4.level.0",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg4_level_1,
            { "MPEG Level 1",                   "btavdtp.codec.mpeg4.level.1",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg4_level_2,
            { "MPEG Level 2",                   "btavdtp.codec.mpeg4.level.2",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg4_level_3,
            { "MPEG4 Level 3",                  "btavdtp.codec.mpeg4.level.3",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_mpeg4_level_rfa,
            { "MPEG4 Level RFA",                "btavdtp.codec.mpeg4.level.rfa",
            FT_UINT8, BASE_HEX, NULL, 0x0F,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_id,
            { "Vendor ID",                      "btavdtp.codec.vendor.vendor_id",
            FT_UINT32, BASE_HEX|BASE_EXT_STRING, &bluetooth_company_id_vals_ext, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_codec_id,
            { "Codec",                          "btavdtp.codec.vendor.codec_id",
            FT_UINT16, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_value,
            { "Value",                          "btavdtp.codec.vendor.value",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_apt_codec_id,
            { "Codec",                          "btavdtp.codec.vendor.codec_id",
            FT_UINT16, BASE_HEX, VALS(vendor_apt_codec_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_sampling_frequency_16000,
            { "Sampling Frequency 16000 Hz",    "btavdtp.codec.aptx.sampling_frequency.16000",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_sampling_frequency_32000,
            { "Sampling Frequency 32000 Hz",    "btavdtp.codec.aptx.sampling_frequency.32000",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_sampling_frequency_44100,
            { "Sampling Frequency 44100 Hz",    "btavdtp.codec.aptx.sampling_frequency.44100",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_sampling_frequency_48000,
            { "Sampling Frequency 48000 Hz",    "btavdtp.codec.aptx.sampling_frequency.48000",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_channel_mode_mono,
            { "Channel Mode Mono",              "btavdtp.codec.aptx.channel_mode.mono",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_channel_mode_dual_channel,
            { "Channel Mode Dual Channel",      "btavdtp.codec.aptx.channel_mode.dual_channel",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_channel_mode_stereo,
            { "Channel Mode Stereo",            "btavdtp.codec.aptx.channel_mode.stereo",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptx_channel_mode_joint_stereo,
            { "Channel Mode Joint Stereo",      "btavdtp.codec.aptx.channel_mode.joint_stereo",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_16000,
            { "Sampling Frequency 16000 Hz",    "btavdtp.codec.aptxhd.sampling_frequency.16000",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_32000,
            { "Sampling Frequency 32000 Hz",    "btavdtp.codec.aptxhd.sampling_frequency.32000",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_44100,
            { "Sampling Frequency 44100 Hz",    "btavdtp.codec.aptxhd.sampling_frequency.44100",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_sampling_frequency_48000,
            { "Sampling Frequency 48000 Hz",    "btavdtp.codec.aptxhd.sampling_frequency.48000",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_channel_mode_mono,
            { "Channel Mode Mono",              "btavdtp.codec.aptxhd.channel_mode.mono",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_channel_mode_dual_channel,
            { "Channel Mode Dual Channel",      "btavdtp.codec.aptxhd.channel_mode.dual_channel",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_channel_mode_stereo,
            { "Channel Mode Stereo",            "btavdtp.codec.aptxhd.channel_mode.stereo",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_channel_mode_joint_stereo,
            { "Channel Mode Joint Stereo",      "btavdtp.codec.aptxhd.channel_mode.joint_stereo",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_aptxhd_rfa,
            { "RFA",                            "btavdtp.codec.aptxhd.rfa",
            FT_UINT32, BASE_HEX, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_rfa1,
            { "RFA1",                           "btavdtp.codec.ldac.rfa1",
            FT_UINT8, BASE_HEX, NULL, 0xC0,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_codec_id,
            { "Codec",                          "btavdtp.codec.vendor.codec_id",
            FT_UINT16, BASE_HEX, VALS(vendor_ldac_codec_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_sampling_frequency_44100,
            { "Sampling Frequency 44100 Hz",    "btavdtp.codec.ldac.sampling_frequency.44100",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_sampling_frequency_48000,
            { "Sampling Frequency 48000 Hz",    "btavdtp.codec.ldac.sampling_frequency.48000",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_sampling_frequency_88200,
            { "Sampling Frequency 88200 Hz",    "btavdtp.codec.ldac.sampling_frequency.88200",
            FT_BOOLEAN, 8, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_sampling_frequency_96000,
            { "Sampling Frequency 96000 Hz",    "btavdtp.codec.ldac.sampling_frequency.96000",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_sampling_frequency_176400,
            { "Sampling Frequency 176400 Hz",    "btavdtp.codec.ldac.sampling_frequency.176400",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_sampling_frequency_192000,
            { "Sampling Frequency 192000 Hz",    "btavdtp.codec.ldac.sampling_frequency.192000",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_rfa2,
            { "RFA2",                           "btavdtp.codec.ldac.rfa2",
            FT_UINT8, BASE_HEX, NULL, 0xF8,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_channel_mode_mono,
            { "Channel Mode Mono",              "btavdtp.codec.ldac.channel_mode.mono",
            FT_BOOLEAN, 8, NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_channel_mode_dual_channel,
            { "Channel Mode Dual Channel",      "btavdtp.codec.ldac.channel_mode.dual_channel",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btavdtp_vendor_specific_ldac_channel_mode_stereo,
            { "Channel Mode Stereo",            "btavdtp.codec.ldac.channel_mode.stereo",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btavdtp_capabilities,
            { "Capabilities",                   "btavdtp.capabilities",
            FT_NONE, BASE_NONE, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_btavdtp_service,
            { "Service",                        "btavdtp.service",
            FT_NONE, BASE_NONE, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_btavdtp_service_multiplexing_entry,
            { "Entry",                          "btavdtp.service_multiplexing_entry",
            FT_NONE, BASE_NONE, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_btavdtp_data,
            { "Data",                           "btavdtp.data",
            FT_NONE, BASE_NONE, NULL, 0x0,
            NULL, HFILL }
        }
    };

    static gint *ett[] = {
        &ett_btavdtp,
        &ett_btavdtp_sep,
        &ett_btavdtp_capabilities,
        &ett_btavdtp_service,
    };

    proto_btavdtp = proto_register_protocol("Bluetooth AVDTP Protocol", "BT AVDTP", "btavdtp");
    btavdtp_handle = register_dissector("btavdtp", dissect_btavdtp, proto_btavdtp);

    proto_register_field_array(proto_btavdtp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    module = prefs_register_protocol_subtree("Bluetooth", proto_btavdtp, NULL);
    prefs_register_static_text_preference(module, "avdtp.version",
            "Bluetooth Protocol AVDTP version: 1.3",
            "Version of protocol supported by this dissector.");

    channels             = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
    sep_list             = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
    sep_open             = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
    media_packet_times   = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
#if RTP_PLAYER_WORKAROUND == TRUE
    file_scope_stream_number = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
#endif
}

void
proto_reg_handoff_btavdtp(void)
{
    dissector_add_string("bluetooth.uuid", "19", btavdtp_handle);

    dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_AVDTP, btavdtp_handle);

    dissector_add_for_decode_as("btl2cap.cid", btavdtp_handle);
}


static gint
dissect_aptx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    proto_item          *aptx_item;
    proto_tree          *aptx_tree;
    proto_item          *pitem;
    bta2dp_codec_info_t *info;
    gdouble              cumulative_frame_duration = 0;

    info = (bta2dp_codec_info_t *) data;

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "aptX");

    switch (pinfo->p2p_dir) {

    case P2P_DIR_SENT:
        col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
        break;

    case P2P_DIR_RECV:
        col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
        break;

    case P2P_DIR_UNKNOWN:
        col_clear(pinfo->cinfo, COL_INFO);
        break;

    default:
        col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown direction %d ",
            pinfo->p2p_dir);
        break;
    }

    col_append_fstr(pinfo->cinfo, COL_INFO, "aptX");

    aptx_item = proto_tree_add_item(tree, proto_aptx, tvb, 0, -1, ENC_NA);
    aptx_tree = proto_item_add_subtree(aptx_item, ett_aptx);

    proto_tree_add_item(aptx_tree, hf_aptx_data, tvb, 0, -1, ENC_NA);

    if (info && info->configuration && info->configuration_length >= 9) {
        gboolean fail = FALSE;
        gdouble expected_speed_data;
        gdouble frame_duration;
        gdouble frame_length = 2 * 2 * 4;
        gint number_of_channels;
        gint frequency;
        gint sample_bits;

        switch (info->configuration[8] >> 4) {
        case 0x01:
            frequency = 48000;
            break;
        case 0x02:
            frequency = 44100;
            break;
        case 0x04:
            frequency = 32000;
            break;
        case 0x08:
            frequency = 16000;
            break;
        default:
            fail = TRUE;
        }

        if (fail)
            return tvb_reported_length(tvb);

        switch (info->configuration[8] & 0x0F) {
        case 0x01:
        case 0x02:
        case 0x04:
            number_of_channels = 2;
            break;
        case 0x08:
            number_of_channels = 1;
            break;
        default:
            fail = TRUE;
        }

        if (fail)
            return tvb_reported_length(tvb);

        sample_bits = 16;

        expected_speed_data = frequency * (sample_bits / 8.0) * number_of_channels;
        frame_duration = (((double) frame_length / (double) expected_speed_data) * 1000.0);

        cumulative_frame_duration = (tvb_reported_length(tvb) / 4.0) * frame_duration;

        pitem = proto_tree_add_double(aptx_tree, hf_aptx_cumulative_frame_duration, tvb, 0, 0, cumulative_frame_duration);
        proto_item_set_generated(pitem);

        if (info && info->previous_media_packet_info && info->current_media_packet_info) {
            nstime_t  delta;

            nstime_delta(&delta, &pinfo->abs_ts, &info->previous_media_packet_info->abs_ts);
            pitem = proto_tree_add_double(aptx_tree, hf_aptx_delta_time, tvb, 0, 0, nstime_to_msec(&delta));
            proto_item_set_generated(pitem);

            pitem = proto_tree_add_double(aptx_tree, hf_aptx_avrcp_song_position, tvb, 0, 0, info->previous_media_packet_info->avrcp_song_position);
            proto_item_set_generated(pitem);

            nstime_delta(&delta, &pinfo->abs_ts, &info->previous_media_packet_info->first_abs_ts);
            pitem = proto_tree_add_double(aptx_tree, hf_aptx_delta_time_from_the_beginning, tvb, 0, 0, nstime_to_msec(&delta));
            proto_item_set_generated(pitem);

            if (!pinfo->fd->visited)
                info->current_media_packet_info->cumulative_frame_duration += cumulative_frame_duration;

            pitem = proto_tree_add_double(aptx_tree, hf_aptx_cumulative_duration, tvb, 0, 0, info->previous_media_packet_info->cumulative_frame_duration);
            proto_item_set_generated(pitem);

            pitem = proto_tree_add_double(aptx_tree, hf_aptx_diff, tvb, 0, 0, info->previous_media_packet_info->cumulative_frame_duration - nstime_to_msec(&delta));
            proto_item_set_generated(pitem);
        }
    }

    return tvb_reported_length(tvb);
}

void
proto_register_aptx(void)
{
    static hf_register_info hf[] = {
        { &hf_aptx_data,
            { "Data",                            "aptx.data",
            FT_BYTES, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_aptx_cumulative_frame_duration,
            { "Cumulative Frame Duration",      "aptx.cumulative_frame_duration",
            FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0x00,
            NULL, HFILL }
        },
        { &hf_aptx_delta_time,
            { "Delta time",                      "aptx.delta_time",
            FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0x00,
            NULL, HFILL }
        },
        { &hf_aptx_avrcp_song_position,
            { "AVRCP Song Position",             "aptx.avrcp_song_position",
            FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0x00,
            NULL, HFILL }
        },
        { &hf_aptx_delta_time_from_the_beginning,
            { "Delta time from the beginning",   "aptx.delta_time_from_the_beginning",
            FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0x00,
            NULL, HFILL }
        },
        { &hf_aptx_cumulative_duration,
            { "Cumulative Music Duration",      "aptx.cumulative_music_duration",
            FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0x00,
            NULL, HFILL }
        },
        { &hf_aptx_diff,
            { "Diff",                            "aptx.diff",
            FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0x00,
            NULL, HFILL }
        },
    };

    static gint *ett[] = {
        &ett_aptx
    };

    proto_aptx = proto_register_protocol("aptX Codec", "aptX", "aptx");
    proto_register_field_array(proto_aptx, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    aptx_handle = register_dissector("aptx", dissect_aptx, proto_aptx);
}

static gint
dissect_ldac(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
    proto_item  *ti;
    proto_tree  *ldac_tree;
    proto_item  *pitem;
    proto_tree  *rtree;
    gint        offset = 0;
    guint8      number_of_frames;
    guint8      syncword;
    guint8      byte;
    guint8      cci;
    guint       frequency;
    gint        available;
    gint        ldac_channels;
    gint        counter = 1;
    gint        frame_length;
    gint        frame_sample_size;
    gint        expected_speed_data;

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDAC");

    ti = proto_tree_add_item(tree, proto_ldac, tvb, offset, -1, ENC_NA);
    ldac_tree = proto_item_add_subtree(ti, ett_ldac);

    proto_tree_add_item(ldac_tree, hf_ldac_fragmented,       tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ldac_tree, hf_ldac_starting_packet,  tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ldac_tree, hf_ldac_last_packet,      tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ldac_tree, hf_ldac_rfa,              tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ldac_tree, hf_ldac_number_of_frames, tvb, offset, 1, ENC_BIG_ENDIAN);
    number_of_frames = tvb_get_guint8(tvb, offset) & 0x0F;
    offset += 1;

    while (tvb_reported_length_remaining(tvb, offset) > 0) {
        available = tvb_reported_length_remaining(tvb, offset);

        syncword = tvb_get_guint8(tvb, offset);
        if (syncword != 0xAA) {
            rtree = proto_tree_add_subtree_format(ldac_tree, tvb, offset, 1,
                    ett_ldac_list, NULL, "Frame: %3u/%3u", counter, number_of_frames);
            pitem = proto_tree_add_item(rtree, hf_ldac_syncword, tvb, offset, 1, ENC_BIG_ENDIAN);
            offset += 1;
            expert_add_info(pinfo, pitem, &ei_ldac_syncword);
            break;
        }

        if (available > 1)  {
            byte = tvb_get_guint8(tvb, offset + 1);
            frequency = (byte & 0xE0) >> 5;
            cci = (byte & 0x18)>> 3;
            frame_length = byte & 0x07;
            frame_length <<= 6;
        } else {
            frequency = 0;
            cci = 0;
        }

        if (available > 2)  {
            byte = tvb_get_guint8(tvb, offset + 2);
            frame_length |= (byte & 0xFC) >> 2;
            frame_length +=1;
        } else {
            frame_length = 0;
        }

        rtree = proto_tree_add_subtree_format(ldac_tree, tvb, offset,
                3 + frame_length > available ? available : 3 + frame_length,
                ett_ldac_list, NULL, "Frame: %3u/%3u", counter, number_of_frames);

        if (3 + frame_length > available) {
            expert_add_info(pinfo, rtree, &ei_ldac_truncated_or_bad_length);
        }

        proto_tree_add_item(rtree, hf_ldac_syncword, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;

        if (cci == LDAC_CCI_MONO)
            ldac_channels = 1;
        else
            ldac_channels = 2;

        switch (frequency) {
            case LDAC_FSID_044:
                frequency = 44100;
                frame_sample_size = 128;
                break;
            case LDAC_FSID_048:
                frequency = 48000;
                frame_sample_size = 128;
                break;
            case LDAC_FSID_088:
                frequency = 88200;
                frame_sample_size = 256;
                break;
            case LDAC_FSID_096:
                frequency = 96000;
                frame_sample_size = 256;
                break;
            case LDAC_FSID_176:
                frequency = 176400;
                frame_sample_size = 512;
                break;
            case LDAC_FSID_192:
                frequency = 192000;
                frame_sample_size = 512;
                break;
            default:
                frequency = 0;
                frame_sample_size = 1;
        }

        proto_tree_add_item(rtree, hf_ldac_sampling_frequency, tvb, offset, 1, ENC_BIG_ENDIAN);
        pitem = proto_tree_add_item(rtree, hf_ldac_channel_config_index, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_item_append_text(pitem, ", Number of channels : %d", ldac_channels);
        proto_tree_add_item(rtree, hf_ldac_frame_length_h,  tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;
        proto_tree_add_item(rtree, hf_ldac_frame_length_l,  tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(rtree, hf_ldac_frame_status,  tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;

        proto_tree_add_item(rtree, hf_ldac_data, tvb, offset, frame_length, ENC_NA);
        offset += frame_length;

        expected_speed_data = (8*(frame_length+3) * frequency) / (frame_sample_size*1000);
        pitem = proto_tree_add_uint(rtree, hf_ldac_expected_data_speed, tvb, offset, 0, expected_speed_data);
        proto_item_append_text(pitem, " kbits/sec");
        proto_item_set_generated(pitem);
        counter += 1;
    }

    col_append_fstr(pinfo->cinfo, COL_INFO, " Frames=%u", number_of_frames);

    return offset;
}
void
proto_register_ldac(void)
{
    expert_module_t* expert_ldac;

    static hf_register_info hf[] = {
        { &hf_ldac_fragmented,
            { "Fragmented",                      "ldac.fragmented",
            FT_BOOLEAN, 8, NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_ldac_starting_packet,
            { "Starting Packet",                 "ldac.starting_packet",
            FT_BOOLEAN, 8, NULL, 0x40,
            NULL, HFILL }
        },
        { &hf_ldac_last_packet,
            { "Last Packet",                     "ldac.last_packet",
            FT_BOOLEAN, 8, NULL, 0x20,
            NULL, HFILL }
        },
        { &hf_ldac_rfa,
            { "RFA",                             "ldac.rfa",
            FT_BOOLEAN, 8, NULL, 0x10,
            NULL, HFILL }
        },
        { &hf_ldac_number_of_frames,
            { "Number of Frames",                "ldac.number_of_frames",
            FT_UINT8, BASE_DEC, NULL, 0x0F,
            NULL, HFILL }
        },
        { &hf_ldac_syncword,
            { "Sync Word",                       "ldac.syncword",
            FT_UINT8, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_ldac_sampling_frequency,
            { "Sampling Frequency",              "ldac.sampling_frequency",
            FT_UINT8, BASE_HEX, VALS(ldac_sampling_frequency_vals), 0xE0,
            NULL, HFILL }
        },
        { &hf_ldac_channel_config_index,
            { "Channel Config Index",            "ldac.channel_config_index",
            FT_UINT8, BASE_HEX, VALS(ldac_channel_config_index_vals), 0x18,
            NULL, HFILL }
        },
        { &hf_ldac_frame_length_h,
            { "Frame Length Index(H)",           "ldac.frame_length_index_H",
              FT_UINT8, BASE_HEX, NULL, 0x07,
            NULL, HFILL }
        },
        { &hf_ldac_frame_length_l,
            { "Frame Length Index(L)",           "ldac.frame_length_index_L",
              FT_UINT8, BASE_HEX, NULL, 0xFC,
            NULL, HFILL }
        },
        { &hf_ldac_frame_status,
            { "Frame Status",                    "ldac.frame_status",
            FT_UINT8, BASE_DEC, NULL, 0x03,
            NULL, HFILL }
        },
        { &hf_ldac_expected_data_speed,
            { "Bitrate",             "ldac.expected_speed_data",
            FT_UINT32, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_ldac_data,
            { "Frame Data",                      "ldac.data",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
    };

    static gint *ett[] = {
        &ett_ldac,
        &ett_ldac_list,
    };

    static ei_register_info ei[] = {
        { &ei_ldac_syncword, { "ldac.syncword.unexpected", PI_PROTOCOL, PI_WARN, "Unexpected syncword", EXPFILL }},
        { &ei_ldac_truncated_or_bad_length, { "ldac.data.truncated", PI_PROTOCOL, PI_WARN, "Either bad frame length or data truncated", EXPFILL }},
    };

    proto_ldac = proto_register_protocol("LDAC Codec", "LDAC", "ldac");

    proto_register_field_array(proto_ldac, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
    expert_ldac = expert_register_protocol(proto_ldac);
    expert_register_field_array(expert_ldac, ei, array_length(ei));

    ldac_handle = register_dissector("ldac", dissect_ldac, proto_ldac);

}

static gint
dissect_bta2dp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    proto_item          *ti;
    proto_tree          *bta2dp_tree;
    proto_item          *pitem;
    gint                 offset = 0;
    dissector_handle_t   codec_dissector = NULL;
    bta2dp_codec_info_t  bta2dp_codec_info;
    sep_data_t           sep_data;
    gboolean             no_avdtp_session;

    no_avdtp_session = (proto_btavdtp != (gint) GPOINTER_TO_UINT(wmem_list_frame_data(
                wmem_list_frame_prev(wmem_list_tail(pinfo->layers)))));

    sep_data.codec = CODEC_SBC;
    sep_data.content_protection_type = 0;
    sep_data.acp_seid = 0;
    sep_data.int_seid = 0;
    sep_data.previous_media_packet_info = NULL;
    sep_data.current_media_packet_info = NULL;
    sep_data.stream_start_in_frame = 0;
    sep_data.stream_end_in_frame = 0;
    sep_data.stream_number = 1;
    sep_data.vendor_id = 0;
    sep_data.vendor_codec = 0;
    sep_data.configuration_length = 0;
    sep_data.configuration = NULL;

    if (force_a2dp_scms_t || force_a2dp_codec != CODEC_DEFAULT) {
        if (force_a2dp_scms_t)
            sep_data.content_protection_type = 2;
        else if (data && !no_avdtp_session)
            sep_data.content_protection_type = ((sep_data_t *) data)->content_protection_type;

        if (force_a2dp_codec != CODEC_DEFAULT)
            sep_data.codec = force_a2dp_codec;
        else if (data && !no_avdtp_session)
            sep_data.codec = ((sep_data_t *) data)->codec;
    } else {
        if (data && !no_avdtp_session)
            sep_data = *((sep_data_t *) data);
    }

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "A2DP");

    switch (pinfo->p2p_dir) {

    case P2P_DIR_SENT:
        col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
        break;

    case P2P_DIR_RECV:
        col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
        break;

    case P2P_DIR_UNKNOWN:
        col_clear(pinfo->cinfo, COL_INFO);
        break;

    default:
        col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown direction %d ",
            pinfo->p2p_dir);
        break;
    }

    ti = proto_tree_add_item(tree, proto_bta2dp, tvb, offset, -1, ENC_NA);
    col_append_fstr(pinfo->cinfo, COL_INFO, "Audio stream - %s",
            val_to_str_const(sep_data.codec, media_codec_audio_type_vals, "unknown codec"));

    bta2dp_tree = proto_item_add_subtree(ti, ett_bta2dp);

    pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_acp_seid, tvb, 0, 0, sep_data.acp_seid);
    proto_item_set_generated(pitem);

    pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_int_seid, tvb, 0, 0, sep_data.int_seid);
    proto_item_set_generated(pitem);

    pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_codec, tvb, 0, 0, sep_data.codec);
    proto_item_set_generated(pitem);

    if (sep_data.codec == 0xFF) { /* Vendor Specific Codec */
        pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_vendor_id, tvb, 0, 0, sep_data.vendor_id);
        proto_item_set_generated(pitem);

        pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_vendor_codec_id, tvb, 0, 0, sep_data.vendor_codec);
        proto_item_set_generated(pitem);

        if ((sep_data.vendor_id == 0x004F && sep_data.vendor_codec == CODECID_APT_X) ||
                (sep_data.vendor_id == 0x00D7 && sep_data.vendor_codec == CODECID_APT_X_HD))
            codec_dissector = aptx_handle;

        if (sep_data.vendor_id == 0x012D && sep_data.vendor_codec == 0x00AA)
            codec_dissector = ldac_handle;
    }

    if (sep_data.content_protection_type > 0) {
        pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_content_protection, tvb, 0, 0, sep_data.content_protection_type);
        proto_item_set_generated(pitem);
    }

    if (sep_data.stream_start_in_frame > 0) {
        pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_stream_start_in_frame, tvb, 0, 0, sep_data.stream_start_in_frame);
        proto_item_set_generated(pitem);
    }

    if (sep_data.stream_end_in_frame > 0) {
        pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_stream_end_in_frame, tvb, 0, 0, sep_data.stream_end_in_frame);
        proto_item_set_generated(pitem);
    }

    pitem = proto_tree_add_uint(bta2dp_tree, hf_bta2dp_stream_number, tvb, 0, 0, sep_data.stream_number);
    proto_item_set_generated(pitem);

    switch (sep_data.codec) {
        case CODEC_SBC:
            codec_dissector = sbc_handle;
            break;
         case CODEC_MPEG12_AUDIO:
            codec_dissector = mp2t_handle;
            break;
        case CODEC_MPEG24_AAC:
            codec_dissector = mpeg_audio_handle;
            break;
        case CODEC_ATRAC:
            codec_dissector = atrac_handle;
            break;
        case CODEC_APT_X:
        case CODEC_APT_X_HD:
            codec_dissector = aptx_handle;
            break;
        case CODEC_LDAC:
            codec_dissector = ldac_handle;
            break;
    }

    bta2dp_codec_info.codec_dissector            = codec_dissector;
    bta2dp_codec_info.configuration_length       = sep_data.configuration_length;
    bta2dp_codec_info.configuration              = sep_data.configuration;
    bta2dp_codec_info.content_protection_type    = sep_data.content_protection_type;
    bta2dp_codec_info.previous_media_packet_info = sep_data.previous_media_packet_info;
    bta2dp_codec_info.current_media_packet_info  = sep_data.current_media_packet_info;

#if RTP_PLAYER_WORKAROUND == TRUE
    /* XXX: Workaround to get multiple RTP streams, because conversations are too
       weak to recognize Bluetooth streams (key is: guint32 interface_id, guint32 adapter_id, guint32 chandle, guint32 cid, guint32 direction -> guint32 stream_number) */
    pinfo->srcport = sep_data.stream_number;
    pinfo->destport = sep_data.stream_number;
#endif

    if (bta2dp_codec_info.content_protection_type == 0 && codec_dissector == aptx_handle) {
        call_dissector_with_data(aptx_handle, tvb, pinfo, tree, &bta2dp_codec_info);
    } else {
        bluetooth_add_address(pinfo, &pinfo->net_dst, sep_data.stream_number, "BT A2DP", pinfo->num, RTP_MEDIA_AUDIO, &bta2dp_codec_info);
        call_dissector(rtp_handle, tvb, pinfo, tree);
    }
    offset += tvb_reported_length_remaining(tvb, offset);

    return offset;
}

void
proto_register_bta2dp(void)
{
    module_t *module;

    static hf_register_info hf[] = {
        { &hf_bta2dp_acp_seid,
            { "ACP SEID",                        "bta2dp.acp_seid",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_bta2dp_int_seid,
            { "INT SEID",                        "bta2dp.int_seid",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_bta2dp_codec,
            { "Codec",                           "bta2dp.codec",
            FT_UINT8, BASE_HEX, VALS(media_codec_audio_type_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_bta2dp_vendor_id,
            { "Vendor ID",                       "bta2dp.codec.vendor.vendor_id",
            FT_UINT32, BASE_HEX|BASE_EXT_STRING, &bluetooth_company_id_vals_ext, 0x00,
            NULL, HFILL }
        },
        { &hf_bta2dp_vendor_codec_id,
            { "Vendor Codec",                    "bta2dp.codec.vendor.codec_id",
            FT_UINT16, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_bta2dp_content_protection,
            { "Content Protection",              "bta2dp.content_protection",
            FT_UINT16, BASE_HEX, VALS(content_protection_type_vals), 0x0,
            NULL, HFILL }
        },
        { &hf_bta2dp_stream_start_in_frame,
            { "Stream Start in Frame",           "bta2dp.stream_start_in_frame",
            FT_FRAMENUM, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_bta2dp_stream_end_in_frame,
            { "Stream End in Frame",           "bta2dp.stream_end_in_frame",
            FT_FRAMENUM, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_bta2dp_stream_number,
            { "Stream Number",                   "bta2dp.stream_number",
            FT_UINT32, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        }
    };

    static gint *ett[] = {
        &ett_bta2dp
    };

    proto_bta2dp = proto_register_protocol("Bluetooth A2DP Profile", "BT A2DP", "bta2dp");
    proto_register_field_array(proto_bta2dp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    bta2dp_handle = register_dissector("bta2dp", dissect_bta2dp, proto_bta2dp);

    module = prefs_register_protocol_subtree("Bluetooth", proto_bta2dp, NULL);
    prefs_register_static_text_preference(module, "a2dp.version",
            "Bluetooth Profile A2DP version: 1.3",
            "Version of profile supported by this dissector.");

    prefs_register_bool_preference(module, "a2dp.content_protection.scms_t",
            "Force SCMS-T decoding",
            "Force decoding stream as A2DP with Content Protection SCMS-T ",
            &force_a2dp_scms_t);

    prefs_register_enum_preference(module, "a2dp.codec",
            "Force codec",
            "Force decoding stream as A2DP with specified codec",
            &force_a2dp_codec, pref_a2dp_codec, FALSE);
}

void
proto_reg_handoff_bta2dp(void)
{
    sbc_handle = find_dissector_add_dependency("sbc", proto_bta2dp);
    mp2t_handle = find_dissector_add_dependency("mp2t", proto_bta2dp);
    mpeg_audio_handle = find_dissector_add_dependency("mpeg-audio", proto_bta2dp);
/* TODO: ATRAC dissector does not exist yet */
    atrac_handle = find_dissector_add_dependency("atrac", proto_bta2dp);

    rtp_handle   = find_dissector_add_dependency("rtp", proto_bta2dp);

    dissector_add_string("bluetooth.uuid", "110a", bta2dp_handle);
    dissector_add_string("bluetooth.uuid", "110b", bta2dp_handle);
    dissector_add_string("bluetooth.uuid", "110d", bta2dp_handle);

    dissector_add_for_decode_as("btl2cap.cid", bta2dp_handle);
}

static gint
dissect_btvdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    proto_item          *ti;
    proto_tree          *btvdp_tree;
    proto_item          *pitem;
    gint                 offset = 0;
    dissector_handle_t   codec_dissector = NULL;
    btvdp_codec_info_t   btvdp_codec_info;
    sep_data_t           sep_data;
    gboolean             no_avdtp_session;

    no_avdtp_session = (proto_btavdtp != (gint) GPOINTER_TO_UINT(wmem_list_frame_data(
                wmem_list_frame_prev(wmem_list_tail(pinfo->layers)))));

    sep_data.codec = CODEC_H263_BASELINE;
    sep_data.content_protection_type = 0;
    sep_data.acp_seid = 0;
    sep_data.int_seid = 0;
    sep_data.previous_media_packet_info = NULL;
    sep_data.current_media_packet_info = NULL;
    sep_data.stream_start_in_frame = 0;
    sep_data.stream_end_in_frame = 0;
    sep_data.stream_number = 1;
    sep_data.vendor_id = 0;
    sep_data.vendor_codec = 0;
    sep_data.configuration_length = 0;
    sep_data.configuration = NULL;

    if (force_vdp_scms_t || force_vdp_codec) {
        if (force_vdp_scms_t)
            sep_data.content_protection_type = 2;
        else if (data  && !no_avdtp_session)
            sep_data.content_protection_type = ((sep_data_t *) data)->content_protection_type;

        if (force_vdp_codec)
            sep_data.codec = force_vdp_codec;
        else if (data  && !no_avdtp_session)
            sep_data.codec = ((sep_data_t *) data)->codec;
    } else {
        if (data  && !no_avdtp_session)
            sep_data = *((sep_data_t *) data);
    }

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "VDP");

    switch (pinfo->p2p_dir) {

    case P2P_DIR_SENT:
        col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
        break;

    case P2P_DIR_RECV:
        col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
        break;

    case P2P_DIR_UNKNOWN:
        col_clear(pinfo->cinfo, COL_INFO);
        break;

    default:
        col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown direction %d ",
            pinfo->p2p_dir);
        break;
    }

    ti = proto_tree_add_item(tree, proto_btvdp, tvb, offset, -1, ENC_NA);
    col_append_fstr(pinfo->cinfo, COL_INFO, "Video stream - %s",
            val_to_str_const(sep_data.codec, media_codec_video_type_vals, "unknown codec"));

    btvdp_tree = proto_item_add_subtree(ti, ett_btvdp);

    pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_acp_seid, tvb, 0, 0, sep_data.acp_seid);
    proto_item_set_generated(pitem);

    pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_int_seid, tvb, 0, 0, sep_data.int_seid);
    proto_item_set_generated(pitem);

    pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_codec, tvb, 0, 0, sep_data.codec);
    proto_item_set_generated(pitem);

    if (sep_data.codec == 0xFF) { /* Vendor Specific Codec */
        pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_vendor_id, tvb, 0, 0, sep_data.vendor_id);
        proto_item_set_generated(pitem);

        pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_vendor_codec_id, tvb, 0, 0, sep_data.vendor_codec);
        proto_item_set_generated(pitem);
    }

    if (sep_data.content_protection_type > 0) {
        pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_content_protection, tvb, 0, 0, sep_data.content_protection_type);
        proto_item_set_generated(pitem);
    }

    if (sep_data.stream_start_in_frame > 0) {
        pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_stream_start_in_frame, tvb, 0, 0, sep_data.stream_start_in_frame);
        proto_item_set_generated(pitem);
    }

    if (sep_data.stream_end_in_frame > 0) {
        pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_stream_end_in_frame, tvb, 0, 0, sep_data.stream_end_in_frame);
        proto_item_set_generated(pitem);
    }

    pitem = proto_tree_add_uint(btvdp_tree, hf_btvdp_stream_number, tvb, 0, 0, sep_data.stream_number);
    proto_item_set_generated(pitem);

    switch (sep_data.codec) {
        case CODEC_H263_BASELINE:
        case CODEC_H263_PROFILE_3:
        case CODEC_H263_PROFILE_8:
            codec_dissector = h263_handle;
            break;
        case CODEC_MPEG4_VSP:
            codec_dissector = mp4v_es_handle;
            break;
    }

    btvdp_codec_info.codec_dissector = codec_dissector;
    btvdp_codec_info.content_protection_type = sep_data.content_protection_type;

#if RTP_PLAYER_WORKAROUND == TRUE
    /* XXX: Workaround to get multiple RTP streams, because conversations are too
       weak to recognize Bluetooth streams (key is: guint32 interface_id, guint32 adapter_id, guint32 chandle, guint32 cid, guint32 direction -> guint32 stream_number) */
    pinfo->srcport = sep_data.stream_number;
    pinfo->destport = sep_data.stream_number;
#endif

    bluetooth_add_address(pinfo, &pinfo->net_dst, 0, "BT VDP", pinfo->num, RTP_MEDIA_VIDEO, &btvdp_codec_info);
    call_dissector(rtp_handle, tvb, pinfo, tree);
    offset += tvb_reported_length_remaining(tvb, offset);

    return offset;
}

void
proto_register_btvdp(void)
{
    module_t *module;
    expert_module_t* expert_btavdtp;

    static hf_register_info hf[] = {
        { &hf_btvdp_acp_seid,
            { "ACP SEID",                        "btvdp.acp_seid",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btvdp_int_seid,
            { "INT SEID",                        "btvdp.int_seid",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btvdp_codec,
            { "Codec",                           "btvdp.codec",
            FT_UINT8, BASE_HEX, VALS(media_codec_video_type_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btvdp_vendor_id,
            { "Vendor ID",                       "btvdp.codec.vendor.vendor_id",
            FT_UINT32, BASE_HEX|BASE_EXT_STRING, &bluetooth_company_id_vals_ext, 0x00,
            NULL, HFILL }
        },
        { &hf_btvdp_vendor_codec_id,
            { "Vendor Codec",                    "btvdp.codec.vendor.codec_id",
            FT_UINT16, BASE_HEX, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btvdp_content_protection,
            { "Content Protection",              "btvdp.content_protection",
            FT_UINT16, BASE_HEX, VALS(content_protection_type_vals), 0x0,
            NULL, HFILL }
        },
        { &hf_btvdp_stream_start_in_frame,
            { "Stream Start in Frame",           "btvdp.stream_start_in_frame",
            FT_FRAMENUM, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btvdp_stream_end_in_frame,
            { "Stream End in Frame",             "btvdp.stream_end_in_frame",
            FT_FRAMENUM, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btvdp_stream_number,
            { "Stream Number",                   "btvdp.stream_number",
            FT_UINT32, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
    };

    static gint *ett[] = {
        &ett_btvdp
    };

    static ei_register_info ei[] = {
        { &ei_btavdtp_sbc_min_bitpool_out_of_range, { "btavdtp.codec.sbc.minimum_bitpool.out_of_range", PI_PROTOCOL, PI_WARN, "Bitpool is out of range. Should be 2..250.", EXPFILL }},
        { &ei_btavdtp_sbc_max_bitpool_out_of_range, { "btavdtp.codec.sbc.maximum_bitpool.out_of_range", PI_PROTOCOL, PI_WARN, "Bitpool is out of range. Should be 2..250.", EXPFILL }},
        { &ei_btavdtp_unexpected_losc_data, { "btavdtp.unexpected_losc_data", PI_PROTOCOL, PI_WARN, "Unexpected losc data", EXPFILL }},
    };

    proto_btvdp = proto_register_protocol("Bluetooth VDP Profile", "BT VDP", "btvdp");
    btvdp_handle = register_dissector("btvdp", dissect_btvdp, proto_btvdp);
    proto_register_field_array(proto_btvdp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
    expert_btavdtp = expert_register_protocol(proto_btvdp);
    expert_register_field_array(expert_btavdtp, ei, array_length(ei));

    module = prefs_register_protocol_subtree("Bluetooth", proto_btvdp, NULL);
    prefs_register_static_text_preference(module, "vdp.version",
            "Bluetooth Profile VDP version: 1.1",
            "Version of profile supported by this dissector.");

    prefs_register_bool_preference(module, "vdp.content_protection.scms_t",
            "Force SCMS-T decoding",
            "Force decoding stream as VDP with Content Protection SCMS-T ",
            &force_vdp_scms_t);

    prefs_register_enum_preference(module, "vdp.codec",
            "Force codec",
            "Force decoding stream as VDP with specified codec",
            &force_vdp_codec, pref_vdp_codec, FALSE);
}

void
proto_reg_handoff_btvdp(void)
{
    h263_handle = find_dissector_add_dependency("h263", proto_btvdp);
    mp4v_es_handle = find_dissector_add_dependency("mp4v-es", proto_btvdp);

    rtp_handle   = find_dissector_add_dependency("rtp", proto_btvdp);

    dissector_add_string("bluetooth.uuid", "1303", btvdp_handle);
    dissector_add_string("bluetooth.uuid", "1304", btvdp_handle);
    dissector_add_string("bluetooth.uuid", "1305", btvdp_handle);

    dissector_add_for_decode_as("btl2cap.cid", btvdp_handle);
}



static gint
dissect_a2dp_cp_scms_t(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
    proto_item  *main_item;
    proto_tree  *main_tree;
    gint         offset = 0;

    main_item = proto_tree_add_item(tree, proto_bta2dp_cph_scms_t, tvb, offset, 1, ENC_NA);
    main_tree = proto_item_add_subtree(main_item, ett_bta2dp_cph_scms_t);

    proto_tree_add_item(main_tree, hf_bta2dp_reserved , tvb, offset, 1, ENC_NA);
    proto_tree_add_item(main_tree, hf_bta2dp_cp_bit, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(main_tree, hf_bta2dp_l_bit , tvb, offset, 1, ENC_NA);
    offset += 1;

    return offset;
}

void
proto_register_bta2dp_content_protection_header_scms_t(void)
{
    static hf_register_info hf[] = {
        { &hf_bta2dp_l_bit,
            { "L-bit",                           "bta2dp.content_protection_header.scms_t.l_bit",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_bta2dp_cp_bit,
            { "Cp-bit",                          "bta2dp.content_protection_header.scms_t.cp_bit",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_bta2dp_reserved,
            { "Reserved",                        "bta2dp.content_protection_header.scms_t.reserved",
            FT_BOOLEAN, 8, NULL, 0xFC,
            NULL, HFILL }
        }
    };

    static gint *ett[] = {
        &ett_bta2dp_cph_scms_t
    };

    proto_bta2dp_cph_scms_t = proto_register_protocol("Bluetooth A2DP Content Protection Header SCMS-T", "BT A2DP Content Protection Header SCMS-T", "bta2dp_content_protection_header_scms_t");
    proto_register_field_array(proto_bta2dp_cph_scms_t, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    register_dissector("bta2dp_content_protection_header_scms_t", dissect_a2dp_cp_scms_t, proto_bta2dp_cph_scms_t);
}

static gint
dissect_vdp_cp_scms_t(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
    proto_item  *main_item;
    proto_tree  *main_tree;
    gint         offset = 0;

    main_item = proto_tree_add_item(tree, proto_btvdp_cph_scms_t, tvb, offset, 1, ENC_NA);
    main_tree = proto_item_add_subtree(main_item, ett_btvdp_cph_scms_t);

    proto_tree_add_item(main_tree, hf_btvdp_reserved , tvb, offset, 1, ENC_NA);
    proto_tree_add_item(main_tree, hf_btvdp_cp_bit, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(main_tree, hf_btvdp_l_bit , tvb, offset, 1, ENC_NA);
    offset += 1;

    return offset;
}

void
proto_register_btvdp_content_protection_header_scms_t(void)
{
    static hf_register_info hf[] = {
        { &hf_btvdp_l_bit,
            { "L-bit",                           "btvdp.content_protection_header.scms_t.l_bit",
            FT_BOOLEAN, 8, NULL, 0x01,
            NULL, HFILL }
        },
        { &hf_btvdp_cp_bit,
            { "Cp-bit",                          "btvdp.content_protection_header.scms_t.cp_bit",
            FT_BOOLEAN, 8, NULL, 0x02,
            NULL, HFILL }
        },
        { &hf_btvdp_reserved,
            { "Reserved",                        "btvdp.content_protection_header.scms_t.reserved",
            FT_BOOLEAN, 8, NULL, 0xFC,
            NULL, HFILL }
        }
    };

    static gint *ett[] = {
        &ett_btvdp_cph_scms_t
    };

    proto_btvdp_cph_scms_t = proto_register_protocol("Bluetooth VDP Content Protection Header SCMS-T", "BT VDP Content Protection Header SCMS-T", "btvdp_content_protection_header_scms_t");
    proto_register_field_array(proto_btvdp_cph_scms_t, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    register_dissector("btvdp_content_protection_header_scms_t", dissect_vdp_cp_scms_t, proto_btvdp_cph_scms_t);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
