[关闭]
@leaveye 2026-04-28T10:16:52.000000Z 字数 20282 阅读 27

ws-proto

wireshark lua script ws protocol


wsproto.lua

  1. --[[
  2. -- Drop this file into `plugins` folder in Wireshark folder, which
  3. -- should contains `profiles` folder in it.
  4. --]]
  5. -- set UDP ports for the dissectors
  6. local ws_proto = { audio_port = 4600, video_port = 4602, subtitle_port = 4604 }
  7. -----------------------------------------------------------------------------
  8. local base = base
  9. local ByteArray = ByteArray
  10. local Dissector = Dissector
  11. local DissectorTable = DissectorTable
  12. local ENC_GB18030 = ENC_GB18030
  13. local Proto = Proto
  14. local ProtoField = ProtoField
  15. -----------------------------------------------------------------------------
  16. local mpeg1layer2_proto = Dissector.get("mpeg-audio")
  17. local h264raw_proto = Dissector.get("h264_bytestream") or Dissector.get("h264")
  18. local function with_proto(name, description, fn)
  19. local proto = Proto(name, description)
  20. fn(proto)
  21. return proto
  22. end
  23. local function have_prefix(buffer, prefix)
  24. local want_len = #prefix
  25. return buffer:len() >= want_len and buffer:raw(0, want_len) == prefix
  26. end
  27. local function format_bits(data, bitsize, bitmask)
  28. local format_string = ""
  29. for i = 1, bitsize, 1 do
  30. local cur_data, cur_mask = data >> (i - 1), bitmask >> (i - 1)
  31. local value_bit, mask_bit = cur_data & 1, cur_mask & 1
  32. local need_gap = (i & 3) == 1 and i >= 4
  33. format_string = (mask_bit == 0 and "." or (value_bit ~= 0 and "1" or "0"))
  34. .. (need_gap and " " or "")
  35. .. format_string
  36. end
  37. return format_string
  38. end
  39. -----------------------------------------------------------------------------
  40. ws_proto.audio = with_proto("WsAudio", "WS audio proto", function(proto)
  41. local fields = {
  42. index = ProtoField.uint16("WsAudio.index", "Index", base.DEC),
  43. timestamp = ProtoField.double("WsAudio.timestamp", "TS", base.DOUBLE),
  44. len = ProtoField.uint16("WsAudio.len", "Len", base.DEC),
  45. data = ProtoField.bytes("WsAudio.data", "Data"),
  46. }
  47. proto.fields = fields
  48. function proto.dissector(buffer, pinfo, tree)
  49. local subtree = tree:add(proto, buffer(), "WS Audio Packet")
  50. local pkt_index = buffer(2, 2):le_uint()
  51. subtree:append_text((", idx %u"):format(pkt_index))
  52. subtree:add(fields.len, buffer(0, 2), buffer(0, 2):le_uint())
  53. subtree:add(fields.index, buffer(2, 2), pkt_index)
  54. subtree:add(
  55. fields.timestamp,
  56. buffer(4, 4),
  57. tonumber(("%u.%04u"):format(buffer(4, 2):le_uint(), buffer(6, 2):le_uint()))
  58. )
  59. if mpeg1layer2_proto then
  60. mpeg1layer2_proto:call(buffer(8, -1):tvb(), pinfo, tree)
  61. pinfo.cols.protocol:set("Ws:" .. tostring(pinfo.cols.protocol))
  62. pinfo.cols.info:prepend(("idx %u, "):format(pkt_index))
  63. else
  64. subtree:add(fields.data, buffer(8, -1))
  65. pinfo.cols.protocol:set("Ws:Mp2")
  66. pinfo.cols.info:set(("idx %u, %s"):format(pkt_index, "Audio Layer 2"))
  67. end
  68. end
  69. end)
  70. -----------------------------------------------------------------------------
  71. ws_proto.video = with_proto("WsVideo", "WS video proto", function(proto)
  72. local fields = {
  73. data = ProtoField.bytes("WsVideo.data", "Data"),
  74. }
  75. proto.fields = fields
  76. local packet_types = { "normal", "config", "fec", "frame-info" }
  77. local packet_control_bytes = {
  78. { "RESET", "\xFF\xFE\xFE\xFE\xFF\xFE\xFE\xFE" },
  79. { "MODE:Changing", "\xFF\xFE\xFE\xFE\xFF\xFE\xFE\xFA" },
  80. { "MODE:Changed", "\xFF\xFE\xFE\xFE\xFF\xFE\xFE\xFB" },
  81. }
  82. ----------------------------------------------------------------
  83. -- global caches
  84. ----------------------------------------------------------------
  85. local IDX_MASK = 0x1FFF -- 8191
  86. local pkt_cache = {} -- pktno -> meta
  87. local frame_cache = {} -- frame_key -> state
  88. local last_frame = nil
  89. local function clear_cache()
  90. pkt_cache = {}
  91. frame_cache = {}
  92. last_frame = nil
  93. end
  94. local function mod_idx(v)
  95. return v & IDX_MASK
  96. end
  97. local function frame_key_of(idx, seq)
  98. return mod_idx(idx - seq)
  99. end
  100. local function get_frame(frame_key)
  101. local st = frame_cache[frame_key]
  102. if st then
  103. return st
  104. end
  105. st = {
  106. key = frame_key,
  107. want_len = nil,
  108. got_len = 0,
  109. max_seq = -1,
  110. fragments = {}, -- seq -> raw bytes
  111. pktnos = {}, -- seq -> pktno
  112. complete = false,
  113. assembled = nil,
  114. assembled_once = false,
  115. checksum = nil,
  116. timestamp = nil,
  117. src_addr = nil,
  118. }
  119. frame_cache[frame_key] = st
  120. return st
  121. end
  122. local function add_fragment(st, seq, raw, pktno)
  123. if st.fragments[seq] then
  124. return false
  125. end
  126. st.fragments[seq] = raw
  127. st.pktnos[seq] = pktno
  128. st.got_len = st.got_len + #raw
  129. if seq > st.max_seq then
  130. st.max_seq = seq
  131. end
  132. return true
  133. end
  134. local function can_complete(st)
  135. if not st.want_len then
  136. return false
  137. end
  138. if st.got_len < st.want_len then
  139. return false
  140. end
  141. for i = 0, st.max_seq do
  142. if not st.fragments[i] then
  143. return false
  144. end
  145. end
  146. return true
  147. end
  148. local function assemble_frame(st)
  149. if st.assembled then
  150. return st.assembled
  151. end
  152. local full = ByteArray.new()
  153. for i = 0, st.max_seq do
  154. local s = st.fragments[i]
  155. if not s then
  156. return nil
  157. end
  158. full:append(ByteArray.new(s, true))
  159. end
  160. st.assembled = full
  161. st.complete = true
  162. return full
  163. end
  164. ----------------------------------------------------------------
  165. -- control packet
  166. ----------------------------------------------------------------
  167. local function dissector_ctrl_bytes(buffer, pinfo, tree)
  168. for _, check_item in ipairs(packet_control_bytes) do
  169. local name, payload = table.unpack(check_item)
  170. local found = have_prefix(buffer, payload)
  171. if found then
  172. local subtree = tree:add(ws_proto.video, buffer(), "WS Video Control Packet: " .. name)
  173. local description = "Control:" .. name
  174. subtree:add(buffer(0, #payload), description)
  175. pinfo.cols.info:set(description)
  176. return true
  177. end
  178. end
  179. return false
  180. end
  181. ----------------------------------------------------------------
  182. -- frame header parser
  183. ----------------------------------------------------------------
  184. local function parse_frame_header(frame_header, st, pinfo, tree)
  185. local frame_header_len = frame_header:len()
  186. if frame_header_len > 13 then
  187. local payload_len = frame_header(1, 4):le_uint() - frame_header_len
  188. local checksum = frame_header(5, 4):le_uint()
  189. local src_addr = frame_header(9, 4):ipv4()
  190. local timestamp = frame_header(13, 2):le_uint() + 1E-4 * frame_header(15, 2):le_uint()
  191. st.want_len = payload_len
  192. st.checksum = checksum
  193. st.timestamp = timestamp
  194. st.src_addr = tostring(src_addr)
  195. pinfo.cols.info:append((" payload %u IDR src %s"):format(payload_len, src_addr))
  196. local frame_tree = tree:add(frame_header, "IDR frame header")
  197. frame_tree:add(frame_header(0, 1), format_bits(frame_header(0, 1):uint(), 8, 0x01) .. " = IDR frame")
  198. frame_tree:add(frame_header(1, 4), ("Len: header %u payload %u"):format(frame_header_len, payload_len))
  199. frame_tree:add(frame_header(5, 4), ("Checksum: %08X"):format(checksum))
  200. frame_tree:add(frame_header(9, 4), ("Source Address: %s"):format(src_addr))
  201. frame_tree:add(frame_header(13, 4), ("Timestamp: %.6f"):format(timestamp))
  202. else
  203. local payload_len = frame_header(1, 4):le_uint() - frame_header_len
  204. local checksum = frame_header(5, 4):le_uint()
  205. local timestamp = frame_header(9, 2):le_uint() + 1E-4 * frame_header(11, 2):le_uint()
  206. st.want_len = payload_len
  207. st.checksum = checksum
  208. st.timestamp = timestamp
  209. pinfo.cols.info:append((" payload %u"):format(payload_len))
  210. local frame_tree = tree:add(frame_header, "non-IDR frame header")
  211. frame_tree:add(frame_header(0, 1), format_bits(frame_header(0, 1):uint(), 8, 0x01) .. " = P frame")
  212. frame_tree:add(frame_header(1, 4), ("Len: header %u payload %u"):format(frame_header_len, payload_len))
  213. frame_tree:add(frame_header(5, 4), ("Checksum: %08X"):format(checksum))
  214. frame_tree:add(frame_header(9, 4), ("Timestamp: %.6f"):format(timestamp))
  215. end
  216. end
  217. ----------------------------------------------------------------
  218. -- render payload / assembled
  219. ----------------------------------------------------------------
  220. local function render_payload(buffer, payload_tvb, st, seq, pinfo, tree)
  221. --[[
  222. tree:add(
  223. buffer(0, 0),
  224. ("DEBUG frame=%u want=%s got=%u seq=%u complete=%s"):format(
  225. st.key,
  226. tostring(st.want_len),
  227. st.got_len,
  228. seq,
  229. tostring(st.complete)
  230. )
  231. )
  232. --]]
  233. if not h264raw_proto then
  234. tree:add(fields.data, buffer(), ("Fragment %u of frame %u"):format(seq, st.key))
  235. pinfo.cols.protocol:set("Ws:V")
  236. return
  237. end
  238. if st.complete then
  239. local full = assemble_frame(st)
  240. local assembled_tree = tree:add(
  241. proto,
  242. buffer(0, 0),
  243. ("Reassembled frame %u: %u packets, %u bytes"):format(st.key, st.max_seq + 1, st.want_len or 0)
  244. )
  245. local tvb = ByteArray.tvb(full, "Reassembled WS Video Frame")
  246. h264raw_proto:call(tvb, pinfo, assembled_tree)
  247. pinfo.cols.protocol:set("Ws:H264")
  248. return
  249. end
  250. local payload_tree = tree:add(buffer(), ("Frame %u: piece %u, %u bytes"):format(st.key, seq, payload_tvb:len()))
  251. h264raw_proto:call(payload_tvb, pinfo, payload_tree)
  252. pinfo.cols.protocol:set("Ws:H264")
  253. end
  254. ----------------------------------------------------------------
  255. -- dissector
  256. ----------------------------------------------------------------
  257. function proto.init()
  258. clear_cache()
  259. end
  260. function proto.dissector(buffer, pinfo, tree)
  261. if pinfo.number == 1 and not pinfo.visited then
  262. clear_cache()
  263. end
  264. local is_resend = false
  265. local offset = 0
  266. if have_prefix(buffer, "\xFF\xFF\xFF\xFF") then
  267. is_resend = true
  268. offset = 4
  269. elseif dissector_ctrl_bytes(buffer, pinfo, tree) then
  270. return
  271. end
  272. local pkt_tree = tree:add(ws_proto.video, buffer(0, offset + 4), "WS Video Packet Header: ")
  273. pinfo.cols.info:clear()
  274. if is_resend then
  275. pkt_tree:add(buffer(0, offset), "resend mark")
  276. pinfo.cols.info:prepend("RESEND ")
  277. end
  278. local header1 = buffer(offset, 2)
  279. local header2 = buffer(offset + 2, 2)
  280. local header1_data = header1:le_uint()
  281. local header2_data = header2:le_uint()
  282. local not_full = (header1_data & 0x8000) ~= 0
  283. local ext_data = (header1_data & 0x4000) ~= 0
  284. offset = offset + 4
  285. ------------------------------------------------------------
  286. -- ext packet
  287. ------------------------------------------------------------
  288. if ext_data then
  289. local ext_type = (header1_data & 0x3C00) >> 10
  290. local vpkt_type = packet_types[ext_type] or "<unknown>"
  291. local summary = ("ext(%s)"):format(vpkt_type)
  292. pinfo.cols.info:append(summary)
  293. pkt_tree:append_text(summary)
  294. if not_full then
  295. pkt_tree:append_text(" not-full")
  296. end
  297. local pkt_index = header1_data & 0x03FF
  298. local grp_index = header2_data
  299. pkt_tree:add(header1, format_bits(header1_data, 16, 0x8000) .. " = reserved")
  300. pkt_tree:add(header1, format_bits(header1_data, 16, 0x4000) .. " = extended")
  301. pkt_tree:add(header1, format_bits(header1_data, 16, 0x3C00) .. (" = type %s"):format(vpkt_type))
  302. pkt_tree:add(header1, format_bits(header1_data, 16, 0x03FF) .. (" = sub idx %u"):format(pkt_index))
  303. pkt_tree:add(header2, format_bits(header2_data, 16, 0x3FFF) .. (" = grp idx %u"):format(grp_index))
  304. pinfo.cols.info:append((" len %u"):format(buffer:len()))
  305. return
  306. end
  307. ------------------------------------------------------------
  308. -- normal packet
  309. ------------------------------------------------------------
  310. local slot_id = header1_data & 0x3FFF
  311. local seq_id = header2_data & 0x0FFF
  312. local summary = ("idx %u seq %u"):format(slot_id, seq_id)
  313. pinfo.cols.info:append(summary)
  314. pkt_tree:append_text(summary)
  315. if not_full then
  316. pkt_tree:append_text(" not-full")
  317. end
  318. pkt_tree:add(
  319. header1,
  320. format_bits(header1_data, 16, 0x8000) .. (" = %s"):format(not_full and "not full" or "full (1024 byte)")
  321. )
  322. pkt_tree:add(header1, format_bits(header1_data, 16, 0x4000) .. " = normal")
  323. pkt_tree:add(header1, format_bits(header1_data, 16, 0x3FFF) .. (" = idx %u"):format(slot_id))
  324. pkt_tree:add(header2, format_bits(header2_data, 16, 0xFFFF) .. (" = seq %u"):format(seq_id))
  325. pinfo.cols.info:append((" len %u"):format(buffer:len()))
  326. local frame_key = frame_key_of(slot_id, seq_id)
  327. local st = get_frame(frame_key)
  328. local frame_header_len = 0
  329. if seq_id == 0 then
  330. local is_idr_frame = buffer(offset, 1):uint() ~= 0
  331. frame_header_len = is_idr_frame and 17 or 13
  332. local frame_header = buffer(offset, frame_header_len)
  333. parse_frame_header(frame_header, st, pinfo, tree)
  334. offset = offset + frame_header_len
  335. last_frame = st
  336. end
  337. local payload_buf = buffer(offset)
  338. local payload_tvb = payload_buf:tvb()
  339. local payload_raw = payload_tvb:raw()
  340. ------------------------------------------------------------
  341. -- cache packet meta
  342. ------------------------------------------------------------
  343. pkt_cache[pinfo.number] = {
  344. idx = slot_id,
  345. seq = seq_id,
  346. frame_key = frame_key,
  347. pktno = pinfo.number,
  348. payload = payload_raw,
  349. payload_len = #payload_raw,
  350. }
  351. ------------------------------------------------------------
  352. -- merge fragment
  353. ------------------------------------------------------------
  354. add_fragment(st, seq_id, payload_raw, pinfo.number)
  355. if can_complete(st) then
  356. assemble_frame(st)
  357. end
  358. ------------------------------------------------------------
  359. -- render
  360. ------------------------------------------------------------
  361. render_payload(payload_buf, payload_tvb, st, seq_id, pinfo, tree)
  362. end
  363. end)
  364. -----------------------------------------------------------------------------
  365. ws_proto.subtitle = with_proto("WsSubt", "WS subtitle proto", function(proto)
  366. local fields = {
  367. version = ProtoField.uint8("WsSubt.version", "Version", base.DEC),
  368. data = ProtoField.bytes("WsSubt.data", "Data"),
  369. }
  370. proto.fields = fields
  371. local function v1_dissector(buffer, pinfo, subtree)
  372. pinfo.cols.protocol:set("Ws:Subt1")
  373. pinfo.cols.info:set("V1")
  374. subtree:append_text(" Version 1")
  375. local count = buffer(0, 4):le_uint()
  376. subtree:append_text((", total %u items"):format(count))
  377. subtree:add(buffer(0, 4), ("Count: %u"):format(count))
  378. local n_chars = buffer(4, 4):le_uint()
  379. subtree:append_text((" %u chars"):format(n_chars))
  380. subtree:add(buffer(4, 4), ("Chars: %u"):format(n_chars))
  381. local item_offset, item_size = 8, 8
  382. local text_offset = item_offset + count * item_size
  383. local pad_offset = text_offset + n_chars * 2
  384. local text_buffer = buffer(text_offset, n_chars * 2)
  385. local text_past_len = 0
  386. for i = 1, count do
  387. local item_buffer = buffer(item_offset + (i - 1) * item_size, item_size)
  388. local item_tree = subtree:add(item_buffer, ("Item #%u"):format(i - 1))
  389. local item_string = ""
  390. local pos_string = ("(%u/720,%u/576)"):format(item_buffer(0, 2):le_uint(), item_buffer(2, 2):le_uint())
  391. item_tree:add(item_buffer(0, 4), ("Pos: %s"):format(pos_string))
  392. item_string = item_string .. pos_string
  393. local item_text_len = item_buffer(4, 2):le_uint()
  394. item_tree:add(item_buffer(4, 2), ("TextLen: %u"):format(item_text_len))
  395. if item_text_len > 0 then
  396. local item_text_buffer = text_buffer(text_past_len, item_text_len * 2)
  397. local item_text = item_text_buffer:string(ENC_GB18030)
  398. item_tree:add(item_text_buffer, ('Text: "%s"'):format(item_text))
  399. item_string = item_string .. (' "%s"'):format(item_text)
  400. text_past_len = text_past_len + item_text_len * 2
  401. end
  402. end
  403. if pad_offset < buffer:len() then
  404. subtree:add(buffer(pad_offset, -1), "padding")
  405. end
  406. end
  407. local function v2_dissector(buffer, pinfo, subtree)
  408. pinfo.cols.protocol:set("Ws:Subt2")
  409. pinfo.cols.info:set("V2")
  410. subtree:append_text(" Version 2")
  411. subtree:add(buffer(0, 2), "Version: 0x0102")
  412. subtree:add(buffer(2, 6), "<reserved>")
  413. local count = buffer(8, 2):le_uint()
  414. subtree:append_text((", total %u items"):format(count))
  415. subtree:add(buffer(8, 2), ("Count: %u"):format(count))
  416. local items, item_size = {}, 40
  417. local item_offset, font_len, text_len = 10, 0, 0
  418. for i = 1, count do
  419. local item = {}
  420. items[i] = item
  421. local item_buffer = buffer(item_offset + (i - 1) * item_size, item_size)
  422. item.len = item_buffer(0, 4):le_uint()
  423. item.font_off, item.font_len = font_len, item_buffer(16, 4):le_uint()
  424. item.text_off, item.text_len = text_len, item_buffer(36, 4):le_uint()
  425. font_len = font_len + item.font_len
  426. text_len = text_len + item.text_len
  427. end
  428. local text_offset = item_offset + count * item_size
  429. local font_offset = text_offset + text_len
  430. local pad_offset = font_offset + font_len
  431. -- local item_segment = buffer(item_offset, count * item_size)
  432. local text_segment = buffer(text_offset, text_len)
  433. local font_segment = buffer(font_offset, font_len)
  434. if count > 0 then
  435. -- local subtree = subtree:add(item_segment, "Item Segment")
  436. for i = 1, count do
  437. local item, item_string = items[i], ""
  438. local item_buffer = buffer(item_offset + (i - 1) * item_size, item_size)
  439. local item_tree = subtree:add(item_buffer, ("Item #%u"):format(i - 1))
  440. local font_buffer = font_segment:len() <= 0 and font_segment
  441. or font_segment(item.font_off, item.font_len)
  442. local text_buffer = text_segment:len() <= 0 and text_segment
  443. or text_segment(item.text_off, item.text_len)
  444. item_tree:add(item_buffer(0, 4), ("Len: %u"):format(item.len))
  445. item_tree:add(item_buffer(4, 4), ("Dir: %u"):format(item_buffer(4, 4):le_uint()))
  446. local pos_string = ("(%u/720,%u/576)"):format(item_buffer(8, 2):le_uint(), item_buffer(10, 2):le_uint())
  447. item_tree:add(item_buffer(8, 4), ("Pos: %s"):format(pos_string))
  448. item_string = item_string .. pos_string
  449. item_tree:add(item_buffer(12, 4), ("CodePage: %u"):format(item_buffer(12, 4):le_uint()))
  450. item_tree:add(item_buffer(16, 4), ("FontLen: %u"):format(item.font_len))
  451. if item.font_len > 0 then
  452. item_tree:add(font_buffer, ('Font: "%s"'):format(font_buffer:string(ENC_GB18030)))
  453. end
  454. item_tree:add(
  455. item_buffer(20, 4),
  456. ("CharSize: %ux%u"):format(item_buffer(20, 2):le_uint(), item_buffer(22, 2):le_uint())
  457. )
  458. local color_string = ("#%06X"):format(item_buffer(24, 4):le_uint())
  459. item_tree:add(item_buffer(24, 4), ("Color: %s"):format(color_string))
  460. item_string = item_string .. " " .. color_string
  461. item_tree:add(item_buffer(28, 4), ("Rotate: %u"):format(item_buffer(28, 4):le_uint()))
  462. item_tree:add(item_buffer(32, 4), ("PointSize: %u"):format(item_buffer(32, 4):le_uint()))
  463. item_tree:add(item_buffer(36, 4), ("TextLen: %u"):format(item.text_len))
  464. if item.text_len > 0 then
  465. local text_string = text_buffer:string(ENC_GB18030)
  466. item_tree:add(text_buffer, ('Text: "%s"'):format(text_string))
  467. item_string = item_string .. (' "%s"'):format(text_string)
  468. end
  469. item_tree:append_text((": %s"):format(item_string))
  470. pinfo.cols.info:append((", %s"):format(item_string))
  471. end
  472. end
  473. if text_len > 0 then
  474. -- local subtree = subtree:add(text_segment, "Text Segment")
  475. for i = 1, count do
  476. local item = items[i]
  477. local text_buffer = text_segment(item.text_off, item.text_len)
  478. subtree:add(text_buffer, ('Text #%u: "%s"'):format(i - 1, text_buffer:string(ENC_GB18030)))
  479. end
  480. end
  481. if font_len > 0 then
  482. -- local subtree = subtree:add(font_segment, "FontName Segment")
  483. for i = 1, count do
  484. local item = items[i]
  485. local font_buffer = font_segment(item.font_off, item.font_len)
  486. subtree:add(font_buffer, ('FontName #%u: "%s"'):format(i - 1, font_buffer:string(ENC_GB18030)))
  487. end
  488. end
  489. if pad_offset < buffer:len() then
  490. subtree:add(buffer(pad_offset, -1), "padding")
  491. end
  492. end
  493. local function v3_dissector(buffer, pinfo, subtree)
  494. pinfo.cols.protocol:set("Ws:Subt3")
  495. pinfo.cols.info:set("V3")
  496. subtree:append_text(" Version 3")
  497. subtree:add(buffer(0, 2), "Version: 0x0103")
  498. subtree:add(buffer(2, 6), "<reserved>")
  499. local count = buffer(8, 2):le_uint()
  500. subtree:append_text((", total %u items"):format(count))
  501. subtree:add(buffer(8, 2), ("Count: %u"):format(count))
  502. local items, item_size = {}, 26
  503. local item_offset, font_len, text_len = 10, 0, 0
  504. for i = 1, count do
  505. local item = {}
  506. items[i] = item
  507. local item_buffer = buffer(item_offset + (i - 1) * item_size, item_size)
  508. item.len = item_buffer(0, 2):le_uint()
  509. item.font_off, item.font_len = font_len, item_buffer(10, 2):le_uint()
  510. item.text_off, item.text_len = text_len, item_buffer(24, 2):le_uint()
  511. font_len = font_len + item.font_len
  512. text_len = text_len + item.text_len
  513. end
  514. local text_offset = item_offset + count * item_size
  515. local font_offset = text_offset + text_len
  516. local pad_offset = font_offset + font_len
  517. -- local item_segment = buffer(item_offset, count * item_size)
  518. local text_segment = buffer(text_offset, text_len)
  519. local font_segment = buffer(font_offset, font_len)
  520. if count > 0 then
  521. -- local subtree = subtree:add(item_segment, "Item Segment")
  522. for i = 1, count do
  523. local item, item_string = items[i], ""
  524. local item_buffer = buffer(item_offset + (i - 1) * item_size, item_size)
  525. local item_tree = subtree:add(item_buffer, ("Item #%u"):format(i - 1))
  526. local font_buffer = font_segment:len() <= 0 and font_segment
  527. or font_segment(item.font_off, item.font_len)
  528. local text_buffer = text_segment:len() <= 0 and text_segment
  529. or text_segment(item.text_off, item.text_len)
  530. item_tree:add(item_buffer(0, 2), ("Len: %u"):format(item.len))
  531. item_tree:add(item_buffer(2, 2), ("Dir: %u"):format(item_buffer(2, 2):le_uint()))
  532. local pos_string = ("(%.2f%%,%.2f%%)"):format(
  533. item_buffer(4, 2):le_uint() / 100,
  534. item_buffer(6, 2):le_uint() / 100
  535. )
  536. item_tree:add(item_buffer(4, 4), ("Pos: %s"):format(pos_string))
  537. item_string = item_string .. pos_string
  538. item_tree:add(item_buffer(8, 2), ("CodePage: %u"):format(item_buffer(8, 2):le_uint()))
  539. item_tree:add(item_buffer(10, 2), ("FontLen: %u"):format(item.font_len))
  540. if item.font_len > 0 then
  541. item_tree:add(font_buffer, ('Font: "%s"'):format(font_buffer:string(ENC_GB18030)))
  542. end
  543. item_tree:add(
  544. item_buffer(12, 4),
  545. ("CharSize: %ux%u"):format(item_buffer(12, 2):le_uint(), item_buffer(14, 2):le_uint())
  546. )
  547. local color_string = ("#%06X"):format(item_buffer(16, 4):le_uint())
  548. item_tree:add(item_buffer(16, 4), ("Color: %s"):format(color_string))
  549. item_string = item_string .. " " .. color_string
  550. item_tree:add(item_buffer(20, 2), ("Rotate: %u"):format(item_buffer(20, 2):le_uint()))
  551. item_tree:add(item_buffer(22, 2), ("PointSize: %u"):format(item_buffer(22, 2):le_uint()))
  552. item_tree:add(item_buffer(24, 2), ("TextLen: %u"):format(item.text_len))
  553. if item.text_len > 0 then
  554. local text_string = text_buffer:string(ENC_GB18030)
  555. item_tree:add(text_buffer, ('Text: "%s"'):format(text_string))
  556. item_string = item_string .. (' "%s"'):format(text_string)
  557. end
  558. item_tree:append_text((": %s"):format(item_string))
  559. pinfo.cols.info:append((", %s"):format(item_string))
  560. end
  561. end
  562. if text_len > 0 then
  563. -- local subtree = subtree:add(text_segment, "Text Segment")
  564. for i = 1, count do
  565. local item = items[i]
  566. local text_buffer = text_segment(item.text_off, item.text_len)
  567. subtree:add(text_buffer, ('Text #%u: "%s"'):format(i - 1, text_buffer:string(ENC_GB18030)))
  568. end
  569. end
  570. if font_len > 0 then
  571. -- local subtree = subtree:add(font_segment, "FontName Segment")
  572. for i = 1, count do
  573. local item = items[i]
  574. local font_buffer = font_segment(item.font_off, item.font_len)
  575. subtree:add(font_buffer, ('FontName #%u: "%s"'):format(i - 1, font_buffer:string(ENC_GB18030)))
  576. end
  577. end
  578. if pad_offset < buffer:len() then
  579. subtree:add(buffer(pad_offset, -1), "padding")
  580. end
  581. end
  582. function proto.dissector(buffer, pinfo, tree)
  583. local real_dissector = ({
  584. [0x0102] = v2_dissector,
  585. [0x0103] = v3_dissector,
  586. })[buffer(0, 2):le_uint()] or v1_dissector
  587. local subtree = tree:add(proto, buffer(), "WS Subtitle")
  588. real_dissector(buffer, pinfo, subtree)
  589. end
  590. end)
  591. -----------------------------------------------------------------------------
  592. local udp_table = DissectorTable.get("udp.port")
  593. udp_table:add(ws_proto.audio_port, ws_proto.audio)
  594. udp_table:add(ws_proto.video_port, ws_proto.video)
  595. udp_table:add(ws_proto.subtitle_port, ws_proto.subtitle)
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注