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