1#!/usr/bin/env python
2# Copyright 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may not
5# use this file except in compliance with the License. You may obtain a copy of
6# the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations under
14# the License.
15#
16#
17#
18#
19# This script extracts Hearing Aid audio data from btsnoop.
20# Generates a valid audio file which can be played using player like smplayer.
21#
22# Audio File Name Format:
23# [PEER_ADDRESS]-[START_TIMESTAMP]-[AUDIO_TYPE]-[SAMPLE_RATE].[CODEC]
24#
25# Debug Infomation File Name Format:
26# debug_ver_[DEBUG_VERSION]-[PEER_ADDRESS]-[START_TIMESTAMP]-[AUDIO_TYPE]-[SAMPLE_RATE].txt
27#
28# Player:
29# smplayer
30#
31# NOTE:
32# Please make sure you HCI Snoop data file includes the following frames:
33# HearingAid "LE Enhanced Connection Complete", GATT write for Audio Control
34# Point with "Start cmd", and the data frames.
35
36import argparse
37import os
38import struct
39import sys
40import time
41
42IS_SENT = "IS_SENT"
43PEER_ADDRESS = "PEER_ADDRESS"
44CONNECTION_HANDLE = "CONNECTION_HANDLE"
45AUDIO_CONTROL_ATTR_HANDLE = "AUDIO_CONTROL_ATTR_HANDLE"
46START = "START"
47TIMESTAMP_STR_FORMAT = "TIMESTAMP_STR_FORMAT"
48TIMESTAMP_TIME_FORMAT = "TIMESTAMP_TIME_FORMAT"
49CODEC = "CODEC"
50SAMPLE_RATE = "SAMPLE_RATE"
51AUDIO_TYPE = "AUDIO_TYPE"
52DEBUG_VERSION = "DEBUG_VERSION"
53DEBUG_DATA = "DEBUG_DATA"
54AUDIO_DATA_B = "AUDIO_DATA_B"
55
56# Debug packet header struct
57header_list_str = ["Event Processed", "Number Packet Nacked By Peripheral", "Number Packet Nacked By Central"]
58# Debug frame information structs
59data_list_str = [
60    "Event Number", "Overrun", "Underrun", "Skips", "Rendered Audio Frame", "First PDU Option", "Second PDU Option",
61    "Third PDU Option"
62]
63
64AUDIO_CONTROL_POINT_UUID = "f0d4de7e4a88476c9d9f1937b0996cc0"
65SEC_CONVERT = 1000000
66folder = None
67full_debug = False
68simple_debug = False
69
70force_audio_control_attr_handle = None
71default_audio_control_attr_handle = 0x0079
72
73audio_data = {}
74
75#=======================================================================
76# Parse ACL Data Function
77#=======================================================================
78
79#-----------------------------------------------------------------------
80# Parse Hearing Aid Packet
81#-----------------------------------------------------------------------
82
83
84def parse_acl_ha_debug_buffer(data, result):
85    """This function extracts HA debug buffer"""
86    if len(data) < 5:
87        return
88
89    version, data = unpack_data(data, 1)
90    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], DEBUG_VERSION, str(version))
91
92    debug_str = result[TIMESTAMP_TIME_FORMAT]
93    for p in range(3):
94        byte_data, data = unpack_data(data, 1)
95        debug_str = debug_str + ", " + header_list_str[p] + "=" + str(byte_data).rjust(3)
96
97    if full_debug:
98        debug_str = debug_str + "\n" + "|".join(data_list_str) + "\n"
99        while True:
100            if len(data) < 7:
101                break
102            base = 0
103            data_list_content = []
104            for counter in range(6):
105                p = base + counter
106                byte_data, data = unpack_data(data, 1)
107                if p == 1:
108                    data_list_content.append(str(byte_data & 0x03).rjust(len(data_list_str[p])))
109                    data_list_content.append(str((byte_data >> 2) & 0x03).rjust(len(data_list_str[p + 1])))
110                    data_list_content.append(str((byte_data >> 4) & 0x0f).rjust(len(data_list_str[p + 2])))
111                    base = 2
112                else:
113                    data_list_content.append(str(byte_data).rjust(len(data_list_str[p])))
114            debug_str = debug_str + "|".join(data_list_content) + "\n"
115
116    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], DEBUG_DATA, debug_str)
117
118
119def parse_acl_ha_audio_data(data, result):
120    """This function extracts HA audio data."""
121    if len(data) < 2:
122        return
123    # Remove audio packet number
124    audio_data_b = data[1:]
125    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], AUDIO_DATA_B, audio_data_b)
126
127
128def parse_acl_ha_audio_type(data, result):
129    """This function parses HA audio control cmd audio type."""
130    audio_type, data = unpack_data(data, 1)
131    if audio_type is None:
132        return
133    elif audio_type == 0x01:
134        audio_type = "Ringtone"
135    elif audio_type == 0x02:
136        audio_type = "Phonecall"
137    elif audio_type == 0x03:
138        audio_type = "Media"
139    else:
140        audio_type = "Unknown"
141    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], AUDIO_TYPE, audio_type)
142
143
144def parse_acl_ha_codec(data, result):
145    """This function parses HA audio control cmd codec and sample rate."""
146    codec, data = unpack_data(data, 1)
147    if codec == 0x01:
148        codec = "G722"
149        sample_rate = "16KHZ"
150    elif codec == 0x02:
151        codec = "G722"
152        sample_rate = "24KHZ"
153    else:
154        codec = "Unknown"
155        sample_rate = "Unknown"
156    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], CODEC, codec)
157    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], SAMPLE_RATE, sample_rate)
158    parse_acl_ha_audio_type(data, result)
159
160
161def parse_acl_ha_audio_control_cmd(data, result):
162    """This function parses HA audio control cmd is start/stop."""
163    control_cmd, data = unpack_data(data, 1)
164    if control_cmd == 0x01:
165        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], START, True)
166        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], TIMESTAMP_STR_FORMAT,
167                          result[TIMESTAMP_STR_FORMAT])
168        parse_acl_ha_codec(data, result)
169    elif control_cmd == 0x02:
170        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], START, False)
171
172
173#-----------------------------------------------------------------------
174# Parse ACL Packet
175#-----------------------------------------------------------------------
176
177
178def parse_acl_att_long_uuid(data, result):
179    """This function parses ATT long UUID to get attr_handle."""
180    # len (1 byte) + start_attr_handle (2 bytes) + properties (1 byte) +
181    # attr_handle (2 bytes) + long_uuid (16 bytes) = 22 bytes
182    if len(data) < 22:
183        return
184    # skip unpack len, start_attr_handle, properties.
185    data = data[4:]
186    attr_handle, data = unpack_data(data, 2)
187    long_uuid_list = []
188    for p in range(0, 16):
189        long_uuid_list.append("{0:02x}".format(struct.unpack(">B", data[p])[0]))
190    long_uuid_list.reverse()
191    long_uuid = "".join(long_uuid_list)
192    # Check long_uuid is AUDIO_CONTROL_POINT uuid to get the attr_handle.
193    if long_uuid == AUDIO_CONTROL_POINT_UUID:
194        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], AUDIO_CONTROL_ATTR_HANDLE, attr_handle)
195
196
197def parse_acl_opcode(data, result):
198    """This function parses acl data opcode."""
199    # opcode (1 byte) = 1 bytes
200    if len(data) < 1:
201        return
202    opcode, data = unpack_data(data, 1)
203    # Check opcode is 0x12 (write request) and attr_handle is
204    # audio_control_attr_handle for check it is HA audio control cmd.
205    if result[IS_SENT] and opcode == 0x12:
206        if len(data) < 2:
207            return
208        attr_handle, data = unpack_data(data, 2)
209        if attr_handle == \
210            get_audio_control_attr_handle(result[CONNECTION_HANDLE]):
211            parse_acl_ha_audio_control_cmd(data, result)
212    # Check opcode is 0x09 (read response) to parse ATT long UUID.
213    elif not result[IS_SENT] and opcode == 0x09:
214        parse_acl_att_long_uuid(data, result)
215
216
217def parse_acl_handle(data, result):
218    """This function parses acl data handle."""
219    # connection_handle (2 bytes) + total_len (2 bytes) + pdu (2 bytes)
220    # + channel_id (2 bytes) = 8 bytes
221    if len(data) < 8:
222        return
223    connection_handle, data = unpack_data(data, 2)
224    connection_handle = connection_handle & 0x0FFF
225    # skip unpack total_len
226    data = data[2:]
227    pdu, data = unpack_data(data, 2)
228    channel_id, data = unpack_data(data, 2)
229
230    # Check ATT packet or "Coc Data Packet" to get ATT information and audio
231    # data.
232    if connection_handle <= 0x0EFF:
233        if channel_id <= 0x003F:
234            result[CONNECTION_HANDLE] = connection_handle
235            parse_acl_opcode(data, result)
236        elif channel_id >= 0x0040 and channel_id <= 0x007F:
237            result[CONNECTION_HANDLE] = connection_handle
238            sdu, data = unpack_data(data, 2)
239            if pdu - 2 == sdu:
240                if result[IS_SENT]:
241                    parse_acl_ha_audio_data(data, result)
242                else:
243                    if simple_debug:
244                        parse_acl_ha_debug_buffer(data, result)
245
246
247#=======================================================================
248# Parse HCI EVT Function
249#=======================================================================
250
251
252def parse_hci_evt_peer_address(data, result):
253    """This function parses peer address from hci event."""
254    peer_address_list = []
255    address_empty_list = ["00", "00", "00", "00", "00", "00"]
256    for n in range(0, 3):
257        if len(data) < 6:
258            return
259        for p in range(0, 6):
260            peer_address_list.append("{0:02x}".format(struct.unpack(">B", data[p])[0]))
261        # Check the address is empty or not.
262        if peer_address_list == address_empty_list:
263            del peer_address_list[:]
264            data = data[6:]
265        else:
266            break
267    peer_address_list.reverse()
268    peer_address = "_".join(peer_address_list)
269    update_audio_data("", "", PEER_ADDRESS, peer_address)
270    update_audio_data(PEER_ADDRESS, peer_address, CONNECTION_HANDLE, result[CONNECTION_HANDLE])
271
272
273def parse_hci_evt_code(data, result):
274    """This function parses hci event content."""
275    # hci_evt (1 byte) + param_total_len (1 byte) + sub_event (1 byte)
276    # + status (1 byte) + connection_handle (2 bytes) + role (1 byte)
277    # + address_type (1 byte) = 8 bytes
278    if len(data) < 8:
279        return
280    hci_evt, data = unpack_data(data, 1)
281    # skip unpack param_total_len.
282    data = data[1:]
283    sub_event, data = unpack_data(data, 1)
284    status, data = unpack_data(data, 1)
285    connection_handle, data = unpack_data(data, 2)
286    connection_handle = connection_handle & 0x0FFF
287    # skip unpack role, address_type.
288    data = data[2:]
289    # We will directly check it is LE Enhanced Connection Complete or not
290    # for get Connection Handle and Address.
291    if not result[IS_SENT] and hci_evt == 0x3E and sub_event == 0x0A \
292        and status == 0x00 and connection_handle <= 0x0EFF:
293        result[CONNECTION_HANDLE] = connection_handle
294        parse_hci_evt_peer_address(data, result)
295
296
297#=======================================================================
298# Common Parse Function
299#=======================================================================
300
301
302def parse_packet_data(data, result):
303    """This function parses packet type."""
304    packet_type, data = unpack_data(data, 1)
305    if packet_type == 0x02:
306        # Try to check HearingAid audio control packet and data packet.
307        parse_acl_handle(data, result)
308    elif packet_type == 0x04:
309        # Try to check HearingAid connection successful packet.
310        parse_hci_evt_code(data, result)
311
312
313def parse_packet(btsnoop_file):
314    """This function parses packet len, timestamp."""
315    packet_result = {}
316
317    # ori_len (4 bytes) + include_len (4 bytes) + packet_flag (4 bytes)
318    # + drop (4 bytes) + timestamp (8 bytes) = 24 bytes
319    packet_header = btsnoop_file.read(24)
320    if len(packet_header) != 24:
321        return False
322
323    ori_len, include_len, packet_flag, drop, timestamp = \
324        struct.unpack(">IIIIq", packet_header)
325
326    if ori_len == include_len:
327        packet_data = btsnoop_file.read(ori_len)
328        if len(packet_data) != ori_len:
329            return False
330        if packet_flag != 2 and drop == 0:
331            packet_result[IS_SENT] = (packet_flag == 0)
332            packet_result[TIMESTAMP_STR_FORMAT], packet_result[TIMESTAMP_TIME_FORMAT] = convert_time_str(timestamp)
333            parse_packet_data(packet_data, packet_result)
334    else:
335        return False
336
337    return True
338
339
340#=======================================================================
341# Update and DumpData Function
342#=======================================================================
343
344
345def dump_audio_data(data):
346    """This function dumps audio data into file."""
347    file_type = "." + data[CODEC]
348    file_name_list = []
349    file_name_list.append(data[PEER_ADDRESS])
350    file_name_list.append(data[TIMESTAMP_STR_FORMAT])
351    file_name_list.append(data[AUDIO_TYPE])
352    file_name_list.append(data[SAMPLE_RATE])
353    if folder is not None:
354        if not os.path.exists(folder):
355            os.makedirs(folder)
356        audio_file_name = os.path.join(folder, "-".join(file_name_list) + file_type)
357        if data.has_key(DEBUG_VERSION):
358            file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
359            debug_file_name = os.path.join(folder, file_prefix + "-".join(file_name_list) + ".txt")
360    else:
361        audio_file_name = "-".join(file_name_list) + file_type
362        if data.has_key(DEBUG_VERSION):
363            file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
364            debug_file_name = file_prefix + "-".join(file_name_list) + ".txt"
365
366    sys.stdout.write("Start to dump Audio File : %s\n" % audio_file_name)
367    if data.has_key(AUDIO_DATA_B):
368        with open(audio_file_name, "wb+") as audio_file:
369            audio_file.write(data[AUDIO_DATA_B])
370            sys.stdout.write("Finished to dump Audio File: %s\n\n" % audio_file_name)
371    else:
372        sys.stdout.write("Fail to dump Audio File: %s\n" % audio_file_name)
373        sys.stdout.write("There isn't any Hearing Aid audio data.\n\n")
374
375    if simple_debug:
376        sys.stdout.write("Start to dump audio %s Debug File\n" % audio_file_name)
377        if data.has_key(DEBUG_DATA):
378            with open(debug_file_name, "wb+") as debug_file:
379                debug_file.write(data[DEBUG_DATA])
380                sys.stdout.write("Finished to dump Debug File: %s\n\n" % debug_file_name)
381        else:
382            sys.stdout.write("Fail to dump audio %s Debug File\n" % audio_file_name)
383            sys.stdout.write("There isn't any Hearing Aid debug data.\n\n")
384
385
386def update_audio_data(relate_key, relate_value, key, value):
387    """
388  This function records the dump audio file related information.
389  audio_data = {
390    PEER_ADDRESS:{
391      PEER_ADDRESS: PEER_ADDRESS,
392      CONNECTION_HANDLE: CONNECTION_HANDLE,
393      AUDIO_CONTROL_ATTR_HANDLE: AUDIO_CONTROL_ATTR_HANDLE,
394      START: True or False,
395      TIMESTAMP_STR_FORMAT: START_TIMESTAMP_STR_FORMAT,
396      CODEC: CODEC,
397      SAMPLE_RATE: SAMPLE_RATE,
398      AUDIO_TYPE: AUDIO_TYPE,
399      DEBUG_VERSION: DEBUG_VERSION,
400      DEBUG_DATA: DEBUG_DATA,
401      AUDIO_DATA_B: AUDIO_DATA_B
402    },
403    PEER_ADDRESS_2:{
404      PEER_ADDRESS: PEER_ADDRESS,
405      CONNECTION_HANDLE: CONNECTION_HANDLE,
406      AUDIO_CONTROL_ATTR_HANDLE: AUDIO_CONTROL_ATTR_HANDLE,
407      START: True or False,
408      TIMESTAMP_STR_FORMAT: START_TIMESTAMP_STR_FORMAT,
409      CODEC: CODEC,
410      SAMPLE_RATE: SAMPLE_RATE,
411      AUDIO_TYPE: AUDIO_TYPE,
412      DEBUG_VERSION: DEBUG_VERSION,
413      DEBUG_DATA: DEBUG_DATA,
414      AUDIO_DATA_B: AUDIO_DATA_B
415    }
416  }
417  """
418    if key == PEER_ADDRESS:
419        if audio_data.has_key(value):
420            # Dump audio data and clear previous data.
421            update_audio_data(key, value, START, False)
422            # Extra clear CONNECTION_HANDLE due to new connection create.
423            if audio_data[value].has_key(CONNECTION_HANDLE):
424                audio_data[value].pop(CONNECTION_HANDLE, "")
425        else:
426            device_audio_data = {key: value}
427            temp_audio_data = {value: device_audio_data}
428            audio_data.update(temp_audio_data)
429    else:
430        for i in audio_data:
431            if audio_data[i].has_key(relate_key) \
432                and audio_data[i][relate_key] == relate_value:
433                if key == START:
434                    if audio_data[i].has_key(key) and audio_data[i][key]:
435                        dump_audio_data(audio_data[i])
436                    # Clear data except PEER_ADDRESS, CONNECTION_HANDLE and
437                    # AUDIO_CONTROL_ATTR_HANDLE.
438                    audio_data[i].pop(key, "")
439                    audio_data[i].pop(TIMESTAMP_STR_FORMAT, "")
440                    audio_data[i].pop(CODEC, "")
441                    audio_data[i].pop(SAMPLE_RATE, "")
442                    audio_data[i].pop(AUDIO_TYPE, "")
443                    audio_data[i].pop(DEBUG_VERSION, "")
444                    audio_data[i].pop(DEBUG_DATA, "")
445                    audio_data[i].pop(AUDIO_DATA_B, "")
446                elif key == AUDIO_DATA_B or key == DEBUG_DATA:
447                    if audio_data[i].has_key(START) and audio_data[i][START]:
448                        if audio_data[i].has_key(key):
449                            ori_data = audio_data[i].pop(key, "")
450                            value = ori_data + value
451                    else:
452                        # Audio doesn't start, don't record.
453                        return
454                device_audio_data = {key: value}
455                audio_data[i].update(device_audio_data)
456
457
458#=======================================================================
459# Tool Function
460#=======================================================================
461
462
463def get_audio_control_attr_handle(connection_handle):
464    """This function gets audio_control_attr_handle."""
465    # If force_audio_control_attr_handle is set, will use it first.
466    if force_audio_control_attr_handle is not None:
467        return force_audio_control_attr_handle
468
469    # Try to check the audio_control_attr_handle is record into audio_data.
470    for i in audio_data:
471        if audio_data[i].has_key(CONNECTION_HANDLE) \
472            and audio_data[i][CONNECTION_HANDLE] == connection_handle:
473            if audio_data[i].has_key(AUDIO_CONTROL_ATTR_HANDLE):
474                return audio_data[i][AUDIO_CONTROL_ATTR_HANDLE]
475
476    # Return default attr_handle if audio_data doesn't record it.
477    return default_audio_control_attr_handle
478
479
480def unpack_data(data, byte):
481    """This function unpacks data."""
482    if byte == 1:
483        value = struct.unpack(">B", data[0])[0]
484    elif byte == 2:
485        value = struct.unpack(">H", data[1] + data[0])[0]
486    else:
487        value = ""
488    data = data[byte:]
489    return value, data
490
491
492def convert_time_str(timestamp):
493    """This function converts time to string format."""
494    really_timestamp = float(timestamp) / SEC_CONVERT
495    local_timestamp = time.localtime(really_timestamp)
496    dt = really_timestamp - long(really_timestamp)
497    ms_str = "{0:06}".format(int(round(dt * 1000000)))
498
499    str_format = time.strftime("%m_%d__%H_%M_%S", local_timestamp)
500    full_str_format = str_format + "_" + ms_str
501
502    time_format = time.strftime("%m-%d %H:%M:%S", local_timestamp)
503    full_time_format = time_format + "." + ms_str
504    return full_str_format, full_time_format
505
506
507def set_config():
508    """This function is for set config by flag and check the argv is correct."""
509    argv_parser = argparse.ArgumentParser(description="Extracts Hearing Aid audio data from BTSNOOP.")
510    argv_parser.add_argument("BTSNOOP", help="BLUETOOTH BTSNOOP file.")
511    argv_parser.add_argument("-f", "--folder", help="select output folder.", dest="folder")
512    argv_parser.add_argument(
513        "-c1",
514        "--connection-handle1",
515        help="set a fake connection handle 1 to capture \
516                           audio dump.",
517        dest="connection_handle1",
518        type=int)
519    argv_parser.add_argument(
520        "-c2",
521        "--connection-handle2",
522        help="set a fake connection handle 2 to capture \
523                           audio dump.",
524        dest="connection_handle2",
525        type=int)
526    argv_parser.add_argument(
527        "-ns",
528        "--no-start",
529        help="No audio 'Start' cmd is \
530                           needed before extracting audio data.",
531        dest="no_start",
532        default="False")
533    argv_parser.add_argument(
534        "-dc",
535        "--default-codec",
536        help="set a default \
537                           codec.",
538        dest="codec",
539        default="G722")
540    argv_parser.add_argument(
541        "-a",
542        "--attr-handle",
543        help="force to select audio control attr handle.",
544        dest="audio_control_attr_handle",
545        type=int)
546    argv_parser.add_argument(
547        "-d", "--debug", help="dump full debug buffer content.", dest="full_debug", default="False")
548    argv_parser.add_argument(
549        "-sd", "--simple-debug", help="dump debug buffer header content.", dest="simple_debug", default="False")
550    arg = argv_parser.parse_args()
551
552    if arg.folder is not None:
553        global folder
554        folder = arg.folder
555
556    if arg.connection_handle1 is not None and arg.connection_handle2 is not None \
557        and arg.connection_handle1 == arg.connection_handle2:
558        argv_parser.error("connection_handle1 can't be same with \
559                          connection_handle2")
560        exit(1)
561
562    if not (arg.no_start.lower() == "true" or arg.no_start.lower() == "false"):
563        argv_parser.error("-ns/--no-start arg is invalid, it should be true/false.")
564        exit(1)
565
566    if arg.connection_handle1 is not None:
567        fake_name = "ConnectionHandle" + str(arg.connection_handle1)
568        update_audio_data("", "", PEER_ADDRESS, fake_name)
569        update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE, arg.connection_handle1)
570        if arg.no_start.lower() == "true":
571            update_audio_data(PEER_ADDRESS, fake_name, START, True)
572            update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT, "Unknown")
573            update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
574            update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
575            update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
576
577    if arg.connection_handle2 is not None:
578        fake_name = "ConnectionHandle" + str(arg.connection_handle2)
579        update_audio_data("", "", PEER_ADDRESS, fake_name)
580        update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE, arg.connection_handle2)
581        if arg.no_start.lower() == "true":
582            update_audio_data(PEER_ADDRESS, fake_name, START, True)
583            update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT, "Unknown")
584            update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
585            update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
586            update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
587
588    if arg.audio_control_attr_handle is not None:
589        global force_audio_control_attr_handle
590        force_audio_control_attr_handle = arg.audio_control_attr_handle
591
592    global full_debug
593    global simple_debug
594    if arg.full_debug.lower() == "true":
595        full_debug = True
596        simple_debug = True
597    elif arg.simple_debug.lower() == "true":
598        simple_debug = True
599
600    if os.path.isfile(arg.BTSNOOP):
601        return arg.BTSNOOP
602    else:
603        argv_parser.error("BTSNOOP file not found: %s" % arg.BTSNOOP)
604        exit(1)
605
606
607def main():
608    btsnoop_file_name = set_config()
609
610    with open(btsnoop_file_name, "rb") as btsnoop_file:
611        identification = btsnoop_file.read(8)
612        if identification != "btsnoop\0":
613            sys.stderr.write("Check identification fail. It is not correct btsnoop file.")
614            exit(1)
615
616        ver, data_link = struct.unpack(">II", btsnoop_file.read(4 + 4))
617        if (ver != 1) or (data_link != 1002):
618            sys.stderr.write("Check ver or dataLink fail. It is not correct btsnoop file.")
619            exit(1)
620
621        while True:
622            if not parse_packet(btsnoop_file):
623                break
624
625        for i in audio_data:
626            if audio_data[i].get(START, False):
627                dump_audio_data(audio_data[i])
628
629
630if __name__ == "__main__":
631    main()
632