wireshark抓包rtsp

项目需要用RTP/RTSP做视频直播,为了对RTSP协议有个直观的认识,我利用Wirshark和Npcap对RTSP数据包进行抓包分析。

工具软件简介

驱动

WinPcapNpcap都是工业级的网络抓包工具,网络抓包基本都会用到。

由于很长一段时间WinPcap没有更新了,于是Npcap在它的基础上被开发出来 (但是WinPcap最近又更新了)。目前来看,WinPcap不能抓取本地回环地址(Loopback Address),即 127.0.0.1,Npcap可以。

前端

Wireshark是非常好用的网络协议分析软件,它可以把Npcap抓取到的数据包按照各种协议解析,可视化非常棒。

操作过程

建立rtsp服务器

我用了两种方式:

  1. 使用Live555服务器。 “LIVE555 Media Server”是一个开源的RTSP服务器,使用很简单。单文件程序,将视频文件和程序放到同一个文件夹,运行程序就可以通过rtsp协议访问了,例如:rtsp://192.168.1.3:8554/demo.mp4。这种方式可以在同一个局域网中访问。
  1. 使用VLC播放器的流服务。 具体为选择 【媒体->流-> 添加文件-> 串流-> 添加RTSP-> 修改端口路径-> 完善sdp地址】,最后得到一串这样的字符 :sout=#rtp{sdp=rtsp://localhost:8554/demo} :no-sout-all :sout-keep 。这种方式由于防火墙的限制,只能在本机使用。

抓取数据包

使用VLC流服务建立的rtsp服务器的方式。 VLC串流开始后,在打开一个VLC播放器,打开网络串流rtsp://localhost:8554/ 就会看到视频了。但注意,由于是本地回环地址抓包,因此Wireshark选择 Npcap Loopback Adapter 网卡输入(安装完Npcap会生成这个网卡)

这里只介绍使用 LIVE555 Media Server 的方式。打开Wireshark,选择电脑网卡,进入主界面会看到好多ip数据包,我们需要挑选出有用的。在过滤输入框内输入ip.addr==192.168.1.3(192.168.1.3是rtsp服务器的ip),这样就只会显示来自rtsp服务器的数据包。
然后用VLC打开网络串流rtsp://192.168.1.3:8554/demo.mp4,就会抓取到rtsp数据包了。

可以看到,开始建立链接的过程中,类似tcp三次握手的方式,相互确认。其中有一个步骤是服务器向客户端发送sdp描述文件RTSP/SDP协议。查看具体信息会发现有Media Description, name and address (m): video 0 RTP/AVP 96Media Description, name and address (m): audio 0 RTP/AVP 97 分别表示视频流为负载96,音频流为负载97。这和RTP的SDP文件是一样的。也就说明了,RTSP协议会自动发送SDP文件,不用再去手动写一份SDP文件然后用VLC打开。

再往下看,会看到传输的数据,有两种负载类型96和97,从SDP描述信息已经知道96为视频流,所以可以把RTP包进一步解包,然后用H264解码;97为音频流,由于没有找到音频的解码协议,因此没有对RTP包进一步解码。

如果没有看到如上的结果,而是显示的是UDP协议,需要对UDP进一步解码,选中一个UDP包,右键选择解码为RTP即可。

从上面的结果可以看出,RTSP是靠RTSP/SDP传输SDP描述文件,靠RTP协议传输音视频数据,靠RTCP进行控制。

扩展 RTP MPEG_TS 包

RTP服务器一般是直接把h264数据或ac3数据发送出去,客户端根据负载类型对数据进行解码。一般直播会选择这种方式,实时性好。

RTP还有另一种方式发送数据,就是把音频和视频封装到TS容器,然后再发送mpeg ts包。这样实际就是发送的一个完整的视频文件了,只是时间只有几秒而已。

可以把其保存到本地视频文件。保存为本地视频文件具体操作可参考wireshark wiki

Wireshark extension to dump MPEG2 transport stream packets to file, removing the network headers and leaving just an MPEG2 transport stream. To use this script:

  1. Download the attachment mpeg_packets_dump.lua
  2. Save it in the Wireshark home directory e.g. c:\Program Files\Wireshark — as “mpeg_packets_dump.lua”
  3. Edit init.lua in the Wireshark home directory and add the following line: dofile(“mpeg_packets_dump.lua”)
  4. Restart Wireshark to add the extension
  5. Capture some traffic which includes some MPEG transport packets, for example, it has been tested with MPEG transmitted via UDP multicast.
  6. Stop the capture, and select Tools -> Dump MPEG TS Packets
  7. Enter the file where the mpeg stream should be saved.
  8. In order to select only one of many streams, enter a wireshark filter — expression, or you can leave the filter blank.
  9. Press okay. Any MPEG packets in the current capture which were detected by the MPEG dissector and that match your filter will be dumped to your output file

RTP MPEG_TS的方式是不需要SDP描述文件的,SDP描述文件的目的是告诉客户端对哪种类型的负载采用哪种解码方式。看看RTP报头可以发现

RTP协议用一个7bit数据表示负载类型,因此一共可以表示128种。具体可看RTP_payload_formats wiki

可以看到有一些类型是固定的。正如上图显示的负载类型为33,就表示这个RTP包的内容为封装为TS的音视频数据,客户端就可以按照mpeg ts解码了。如果使用的是96以上的负载类型,就需要写一个sdp文件告诉客户端怎么解码了。

ffmpeg命令行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#提取视频:
ffmpeg -i demo.mp4 -vcodec copy -an video.mp4
#提取音频:
ffmpeg -i demo.mp4 -acodec copy -vn Audio.mp3

#rtp 推流
ffmpeg -re -i video.mp4 -vcodec copy -f rtp rtp://127.0.0.1:1234
ffmpeg -re -i Audio.mp3 -acodec copy -f rtp rtp://127.0.0.1:1235

#rtp mpegts 推流
ffmpeg -re -i demo.mp4 -vcodec copy -f rtp_mpegts rtp://127.0.0.1:1236

# 推流到rtsp服务器
ffmpeg -re -i demo.mp4 -rtsp_transport tcp -vcodec copy -f rtsp rtsp://rtsp/test

ffmpeg -re -i demo.mp4 -rtsp_transport udp -vcodec copy -f rtsp rtsp://rtsp/test

wireshark插件

在抓包RTP过程中,常常需要把视频数据保存到本地,我使用到了两个非常好用的插件,可以满足一般需求。分别是 h264_export.lua 和 mpeg_packets_dump.lua 。

安装方式,进入wireshark根目录,找到init.lua文件,在最后面添加两行dofile("mpeg_packets_dump.lua") dofile("h264_export.lua") ,然后把h264_export.lua 和 mpeg_packets_dump.lua文件放到根目录下即可。重启软件就能加载插件了。

  1. h264_export.lua 把H264包保存为本地 .h264视频文件[项目地址] (https://github.com/seudut/h264_export)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    -- Dump RTP h.264 payload to raw h.264 file (*.264)  
    -- According to RFC3984 to dissector H264 payload of RTP to NALU, and write it
    -- to from<sourceIp_sourcePort>to<dstIp_dstPort>.264 file. By now, we support single NALU,
    -- STAP-A and FU-A format RTP payload for H.264.
    -- You can access this feature by menu "Tools->Export H264 to file [HQX's plugins]"
    -- Author: Huang Qiangxiong (qiangxiong.huang@gmail.com)
    -- change log:
    -- 2012-03-13
    -- Just can play
    -- 2012-04-28
    -- Add local to local function, and add [local bit = require("bit")] to prevent
    -- bit recleared in previous file.
    -- 2013-07-11
    -- Add sort RTP and drop uncompleted frame option.
    -- 2013-07-19
    -- Do nothing when tap is triggered other than button event.
    -- Add check for first or last packs lost of one frame.
    -- 2014-10-23
    -- Fixed bug about print a frame.nalu_type error.
    -- 2014-11-07
    -- Add support for Lua 5.2(>1.10.1) and 5.1(<=1.10.1).
    -- Change range:string() to range:raw().
    -- Change h264_f.value to h264_f.range:bytes() because of wireshark lua bug.
    -- 2015-06-03
    -- Fixed bug that if ipv6 address is using the file will not generated.(we replace ':' to '.')
    -- 2017-08-18
    -- Fixed bug on Mac with Wireshark 2.4.1, that error (Permission denied) happens when writing to file.
    -- Add check when open file, if failed, trying home directory instead of default directory
    -- (/Application/Wireshark.app/)
    ------------------------------------------------------------------------------------------------
    do
    --local bit = require("bit") -- only work before 1.10.1
    --local bit = require("bit32") -- only work after 1.10.1 (only support in Lua 5.2)
    local version_str = string.match(_VERSION, "%d+[.]%d*")
    local version_num = version_str and tonumber(version_str) or 5.1
    local bit = (version_num >= 5.2) and require("bit32") or require("bit")

    -- for geting h264 data (the field's value is type of ByteArray)
    local f_h264 = Field.new("h264")
    local f_rtp = Field.new("rtp")
    local f_rtp_seq = Field.new("rtp.seq")
    local f_rtp_timestamp = Field.new("rtp.timestamp")
    local nalu_type_list = {
    [0] = "Unspecified",
    [1] = "P/B_slice",
    [2] = "P/B_A",
    [3] = "P/B_B",
    [4] = "P/B_C",
    [5] = "I_slice",
    [6] = "SEI",
    [7] = "SPS",
    [8] = "PPS",
    [9] = "AUD",
    }

    local function get_enum_name(list, index)
    local value = list[index]
    return value and value or "Unknown"
    end

    -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this function
    local function export_h264_to_file()
    -- window for showing information
    local tw = TextWindow.new("Export H264 to File Info Win")
    --local pgtw = ProgDlg.new("Export H264 to File Process", "Dumping H264 data to file...")
    local pgtw;

    -- add message to information window
    function twappend(str)
    tw:append(str)
    tw:append("\n")
    end

    -- running first time for counting and finding sps+pps, second time for real saving
    local first_run = true
    -- variable for storing rtp stream and dumping parameters
    local stream_infos = nil
    -- drop_uncompleted_frame
    local drop_uncompleted_frame = false
    -- max frame buffer size
    local MAX_FRAME_NUM = 3

    -- trigered by all h264 packats
    local my_h264_tap = Listener.new(tap, "h264")

    -- get rtp stream info by src and dst address
    function get_stream_info(pinfo)
    local key = "from_" .. tostring(pinfo.src) .. "_" .. tostring(pinfo.src_port) .. "to" .. tostring(pinfo.dst) .. "_" .. tostring(pinfo.dst_port) .. (drop_uncompleted_frame and "_dropped" or "_all")
    key = key:gsub(":", ".")
    local stream_info = stream_infos[key]
    if not stream_info then -- if not exists, create one
    stream_info = { }
    stream_info.filename = key.. ".264"
    stream_info.file, err= io.open(stream_info.filename, "wb")
    if stream_info.file == nil then
    twappend("*** Error - Could not open file: " .. err .. "\n")
    twappend("Trying the home directory... \n")
    stream_info.file, err= io.open( os.getenv("HOME") .. "/" .. stream_info.filename, "wb")
    if stream_info.file == nil then
    twappend("*** Error - :" .. err .. " \n")
    else
    twappend("*** Success - Open file : " .. os.getenv("HOME") .. "/" .. stream_info.filename .. " \n")
    end
    end
    stream_info.counter = 0 -- counting h264 total NALUs
    stream_info.counter2 = 0 -- for second time running
    stream_infos[key] = stream_info
    twappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port)
    .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n [" .. stream_info.filename .. "] ...\n")
    end
    return stream_info
    end

    -- write a NALU or part of NALU to file.
    local function real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)
    if first_run then
    stream_info.counter = stream_info.counter + 1

    if begin_with_nalu_hdr then
    -- save SPS or PPS
    local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)
    if not stream_info.sps and nalu_type == 7 then
    stream_info.sps = str_bytes
    elseif not stream_info.pps and nalu_type == 8 then
    stream_info.pps = str_bytes
    end
    end

    else -- second time running
    --[[
    if begin_with_nalu_hdr then
    -- drop AUD
    local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)
    if nalu_type == 9 then
    return;
    end
    end
    ]]

    if stream_info.counter2 == 0 then
    -- write SPS and PPS to file header first
    if stream_info.sps then
    stream_info.file:write("\00\00\00\01")
    stream_info.file:write(stream_info.sps)
    else
    twappend("Not found SPS for [" .. stream_info.filename .. "], it might not be played!\n")
    end
    if stream_info.pps then
    stream_info.file:write("\00\00\00\01")
    stream_info.file:write(stream_info.pps)
    else
    twappend("Not found PPS for [" .. stream_info.filename .. "], it might not be played!\n")
    end
    end

    if begin_with_nalu_hdr then
    -- *.264 raw file format seams that every nalu start with 0x00000001
    stream_info.file:write("\00\00\00\01")
    end
    stream_info.file:write(str_bytes)
    stream_info.counter2 = stream_info.counter2 + 1

    -- update progress window's progress bar
    if stream_info.counter > 0 and stream_info.counter2 < stream_info.counter then pgtw:update(stream_info.counter2 / stream_info.counter) end
    end
    end

    local function comp_pack(p1, p2)
    if math.abs(p2.seq - p1.seq) < 1000 then
    return p1.seq < p2.seq
    else -- seqeunce is over 2^16, so the small one is much big
    return p1.seq > p2.seq
    end
    end

    local function print_seq_error(stream_info, str)
    if stream_info.seq_error_counter == nil then
    stream_info.seq_error_counter = 0
    end
    stream_info.seq_error_counter = stream_info.seq_error_counter + 1
    twappend(str .. " SeqErrCounts=" .. stream_info.seq_error_counter)
    end

    local function sort_and_write(stream_info, frame)
    table.sort(frame.packs, comp_pack)

    -- check if it is uncompleted frame
    local completed = true
    for i = 1, #frame.packs - 1, 1 do
    local seq1 = frame.packs[i].seq
    local seq2 = frame.packs[i+1].seq
    if bit.band(seq1+1, 0xFFFF) ~= seq2 then
    print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq between " .. seq1 .. " and " .. seq2)
    completed = false
    end
    end

    if not frame.packs[1].nalu_begin then
    print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq before " .. frame.packs[1].seq)
    completed = false
    end

    if not frame.packs[#frame.packs].nalu_end then
    print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq after " .. frame.packs[#frame.packs].seq)
    completed = false
    end

    if completed then
    for i = 1, #frame.packs, 1 do
    real_write_to_file(stream_info, frame.packs[i].data, frame.packs[i].nalu_begin)
    end
    else
    twappend(" We drop one uncompleted frame: rtp.timestamp=" .. frame.timestamp
    .. " nalu_type=" .. (frame.nalu_type and frame.nalu_type .."(" .. get_enum_name(nalu_type_list, frame.nalu_type) .. ")" or "unknown") )
    end
    end

    local function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr, timestamp, seq, end_of_nalu)
    if drop_uncompleted_frame and not first_run then -- sort and drop uncompleted frame
    if stream_info.frame_buffer_size == nil then
    stream_info.frame_buffer_size = 0
    end

    if timestamp < 0 or seq < 0 then
    twappend(" Invalid rtp timestamp (".. timestamp .. ") or seq (".. seq .. ")! We have to write it to file directly!")
    real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)
    return;
    end

    -- check if this frame has existed
    local p = stream_info.frame_buffer
    while p do
    if p.timestamp == timestamp then
    break;
    else
    p = p.next
    end
    end

    if p then -- add this pack to frame
    if begin_with_nalu_hdr then
    p.nalu_type = bit.band(str_bytes:byte(1), 0x1F)
    end
    table.insert(p.packs, { ["seq"] = seq, ["data"] = str_bytes , ["nalu_begin"] = begin_with_nalu_hdr, ["nalu_end"] = end_of_nalu })
    return
    end

    if stream_info.frame_buffer_size >= MAX_FRAME_NUM then
    -- write the most early frame to file
    sort_and_write(stream_info, stream_info.frame_buffer)
    stream_info.frame_buffer = stream_info.frame_buffer.next
    stream_info.frame_buffer_size = stream_info.frame_buffer_size - 1
    end

    -- create a new frame buffer for new frame (timestamp)
    local frame = {}
    frame.timestamp = timestamp
    if begin_with_nalu_hdr then
    frame.nalu_type = bit.band(str_bytes:byte(1), 0x1F)
    end
    frame.packs = {{ ["seq"] = seq, ["data"] = str_bytes, ["nalu_begin"] = begin_with_nalu_hdr, ["nalu_end"] = end_of_nalu}} -- put pack to index 1 pos
    frame.next = nil

    if stream_info.frame_buffer_size == 0 then -- first frame
    stream_info.frame_buffer = frame
    else
    p = stream_info.frame_buffer
    while p.next do
    p = p.next
    end
    p.next = frame
    end
    stream_info.frame_buffer_size = stream_info.frame_buffer_size + 1

    else -- write data direct to file without sort or frame drop
    real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)
    end
    end

    -- read RFC3984 about single nalu/stap-a/fu-a H264 payload format of rtp
    -- single NALU: one rtp payload contains only NALU
    local function process_single_nalu(stream_info, h264, timestamp, seq)
    --write_to_file(stream_info, h264:tvb()():string(), true, timestamp, seq, true)
    write_to_file(stream_info, ((version_num >= 5.2) and h264:tvb():raw() or h264:tvb()():string()), true, timestamp, seq, true)
    end

    -- STAP-A: one rtp payload contains more than one NALUs
    local function process_stap_a(stream_info, h264, timestamp, seq)
    local h264tvb = h264:tvb()
    local offset = 1
    local i = 1
    repeat
    local size = h264tvb(offset,2):uint()
    --write_to_file(stream_info, h264tvb(offset+2, size):string(), true, timestamp, i, true)
    write_to_file(stream_info, ((version_num >= 5.2) and h264tvb:raw(offset+2, size) or h264tvb(offset+2, size):string()), true, timestamp, i, true)
    offset = offset + 2 + size
    i = i + 1
    until offset >= h264tvb:len()
    end

    -- FU-A: one rtp payload contains only one part of a NALU (might be begin, middle and end part of a NALU)
    local function process_fu_a(stream_info, h264, timestamp, seq)
    local h264tvb = h264:tvb()
    local fu_idr = h264:get_index(0)
    local fu_hdr = h264:get_index(1)
    local end_of_nalu = (bit.band(fu_hdr, 0x40) ~= 0)
    if bit.band(fu_hdr, 0x80) ~= 0 then
    -- start bit is set then save nalu header and body
    local nalu_hdr = bit.bor(bit.band(fu_idr, 0xE0), bit.band(fu_hdr, 0x1F))
    --write_to_file(stream_info, string.char(nalu_hdr) .. h264tvb(2):string(), true, timestamp, seq, end_of_nalu)
    write_to_file(stream_info, string.char(nalu_hdr) .. ((version_num >= 5.2) and h264tvb:raw(2) or h264tvb(2):string()), true, timestamp, seq, end_of_nalu)
    else
    -- start bit not set, just write part of nalu body
    --write_to_file(stream_info, h264tvb(2):string(), false, timestamp, seq, end_of_nalu)
    write_to_file(stream_info, ((version_num >= 5.2) and h264tvb:raw(2) or h264tvb(2):string()), false, timestamp, seq, end_of_nalu)
    end
    end

    -- call this function if a packet contains h264 payload
    function my_h264_tap.packet(pinfo,tvb)
    if stream_infos == nil then
    -- not triggered by button event, so do nothing.
    return
    end
    local h264s = { f_h264() } -- using table because one packet may contains more than one RTP
    local rtps = { f_rtp() }
    local rtp_seqs = { f_rtp_seq() }
    local rtp_timestamps = { f_rtp_timestamp() }

    for i,h264_f in ipairs(h264s) do
    if h264_f.len < 2 then
    return
    end
    --local h264 = h264_f.value -- is ByteArray, it only works for 1.10.1 or early version
    --local h264 = h264_f.range:bytes() -- according to user-guide.chm, there is a bug of fieldInfo.value, so we have to convert it to TVB range first
    local h264 = (version_num >= 5.2) and h264_f.range:bytes() or h264_f.value
    local hdr_type = bit.band(h264:get_index(0), 0x1F)
    local stream_info = get_stream_info(pinfo)
    --twappend(string.format("hdr_type=%X %d", hdr_type, hdr_type))
    --twappend("bytearray=" .. tostring(h264))
    --twappend("byterange=" .. tostring(h264_f.range):upper())
    -- search the RTP timestamp and sequence of this H264
    local timestamp = -1
    local seq = -1
    -- debug begin
    local rtplen = -1
    local preh264_foffset = -1
    local prertp_foffset = -1
    local preh264len = -1
    -- debug end
    if drop_uncompleted_frame then
    local matchx = 0;
    for j,rtp_f in ipairs(rtps) do
    if h264_f.offset > rtp_f.offset and h264_f.offset - rtp_f.offset <= 16 and h264_f.offset+h264_f.len <= rtp_f.offset+rtp_f.len then
    -- debug begin
    --if h264_f.offset > rtp_f.offset and h264_f.offset < rtp_f.offset+rtp_f.len then
    matchx = matchx + 1
    if matchx > 1 then
    print_seq_error(stream_info, "ASS seq=" .. seq .. " timestamp=" .. timestamp .. " rtplen=" .. rtplen .. " rtpoff=" .. prertp_foffset .. " h264off=" .. preh264_foffset .. " h264len=" .. preh264len .. " |matched=" .. matchx .. " New seq=" .. rtp_seqs[j].value .. " timestamp=" .. rtp_timestamps[j].value .. " rtplen=" .. rtp_f.len .." rtpoff=" .. rtp_f.offset .. " h264off=" .. h264_f.offset .. " h264.len=" .. h264_f.len)
    end
    -- debug end
    seq = rtp_seqs[j].value
    timestamp = rtp_timestamps[j].value
    -- debug begin
    rtplen = rtp_f.len
    preh264_foffset = h264_f.offset
    prertp_foffset = rtp_f.offset
    preh264len = h264_f.len
    -- debug end
    break
    end
    end

    end

    if hdr_type > 0 and hdr_type < 24 then
    -- Single NALU
    process_single_nalu(stream_info, h264, timestamp, seq)
    elseif hdr_type == 24 then
    -- STAP-A Single-time aggregation
    process_stap_a(stream_info, h264, timestamp, seq)
    elseif hdr_type == 28 then
    -- FU-A
    process_fu_a(stream_info, h264, timestamp, seq)
    else
    twappend("Error: unknown type=" .. hdr_type .. " ; we only know 1-23(Single NALU),24(STAP-A),28(FU-A)!")
    end
    end
    end

    -- close all open files
    local function close_all_files()
    if stream_infos then
    local no_streams = true
    for id,stream in pairs(stream_infos) do
    if stream and stream.file then
    if stream.frame_buffer then
    local p = stream.frame_buffer
    while p do
    sort_and_write(stream, p)
    p = p.next
    end
    stream.frame_buffer = nil
    stream.frame_buffer_size = 0
    end
    stream.file:flush()
    stream.file:close()
    twappend("File [" .. stream.filename .. "] generated OK!\n")
    stream.file = nil
    no_streams = false
    end
    end

    if no_streams then
    twappend("Not found any H.264 over RTP streams!")
    end
    end
    end

    function my_h264_tap.reset()
    -- do nothing now
    end

    local function remove()
    my_h264_tap:remove()
    end

    tw:set_atclose(remove)

    local function export_h264(drop_frame)
    pgtw = ProgDlg.new("Export H264 to File Process", "Dumping H264 data to file...")
    first_run = true
    drop_uncompleted_frame = drop_frame
    stream_infos = {}
    -- first time it runs for counting h.264 packets and finding SPS and PPS
    retap_packets()
    first_run = false
    -- second time it runs for saving h264 data to target file.
    retap_packets()
    close_all_files()
    -- close progress window
    pgtw:close()
    stream_infos = nil
    end

    local function export_all()
    export_h264(false)
    end

    local function export_completed_frames()
    export_h264(true)
    end

    tw:add_button("Export All", export_all)
    tw:add_button("Export Completed Frames (Drop uncompleted frames)", export_completed_frames)
    end

    -- Find this feature in menu "Tools->"Export H264 to file [HQX's plugins]""
    register_menu("Export H264 to file [HQX's plugins]", export_h264_to_file, MENU_TOOLS_UNSORTED)
    end
  2. mpeg_packets_dump.lua 将mpeg ts包保存为ts视频文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107

    -- Wireshark extension to dump MPEG2 transport stream packets
    --
    -- To use this script:
    -- 1. Save it in the Wireshark home directory e.g. c:\Program Files\Wireshark
    -- 2. Edit init.lua in the Wireshark home directory and add the following line
    -- dofile("mpeg_packets_dump.lua")
    -- 3. Restart Wireshark to add the extension
    -- 4. Capture some traffic which includes some MPEG transport packets, for
    -- example, it has been tested with MPEG transmitted via UDP multicast.
    -- 5. Stop the capture, and select Tools -> Dump MPEG TS Packets
    -- 6. Enter the file where the mpeg stream should be saved.
    -- 7. In order to select only one of many streams, enter a wireshark filter
    -- expression, or you can leave the filter blank.
    -- 8. Press okay. Any MPEG packets in the current capture which were detected
    -- by the MPEG dissector and that match your filter will be dumped to
    -- your output file.
    --
    -- Tested with Wireshark 1.4.3
    -- ryan.gorsuch_at_echostar_com
    -- 2011-04-01
    -- Modified and tested with Wireshark 1.11.3
    -- hadrielk_at_yahoo_com
    -- 2014-02-17

    -- only works in wireshark, not tshark
    if not GUI_ENABLED then
    print("mpeg_packets_dump.lua only works in Wireshark")
    return
    end

    -- declare some field extractors
    local mpeg_pid = Field.new("mp2t.pid")
    local mpeg_pkt = Field.new("mp2t")

    -- declare some functions we define later
    local tobinary

    -- do a payload dump when prompted by the user
    local function init_payload_dump(file,filter)

    local packet_count = 0
    local tap = Listener.new(nil,filter)
    local myfile = assert(io.open(file, "w+b"))

    -- this function is going to be called once each time our filter matches
    function tap.packet(pinfo,tvb)

    if ( mpeg_pid() ) then
    packet_count = packet_count + 1

    -- there can be multiple mp2t packets in a given frame, so get them all into a table
    local contents = { mpeg_pkt() }

    for i,finfo in ipairs(contents) do
    local tvbrange = finfo.range
    myfile:write( tobinary( tostring( tvbrange:bytes() ) ) )
    myfile:flush()
    end
    end
    end

    -- re-inspect all the packets that are in the current capture, thereby
    -- triggering the above tap.packet function
    retap_packets()

    -- cleanup
    myfile:close()
    tap:remove()
    debug("Dumped mpeg packets: " .. packet_count )
    end

    -- show this dialog when the user select "Dump" from the Tools menu
    local function begin_dialog_menu()
    new_dialog("Dump MPEG TS Packets",init_payload_dump,"Output file","Packet filter (optional)\n\nExamples:\nip.dst == 225.1.1.4\nmp2t\nmp2t.pid == 0x300")
    end

    register_menu("Dump MPEG TS Packets",begin_dialog_menu,MENU_TOOLS_UNSORTED)


    local function hex(ascii_code)
    -- convert an ascii char code to an integer value "0" => 0, "1" => 1, etc
    if not ascii_code then
    return 0
    elseif ascii_code < 58 then
    return ascii_code - 48
    elseif ascii_code < 91 then
    return ascii_code - 65 + 10
    else
    return ascii_code - 97 + 10
    end
    end

    tobinary = function (hexbytes)
    -- this function converts a hex-string to raw bytes

    local binary = {}
    local sz = 1

    for i=1, string.len(hexbytes), 2 do
    binary[sz] = string.char( 16 * hex( string.byte(hexbytes,i) ) + hex( string.byte(hexbytes,i+1) ) )
    sz = sz + 1
    end

    return table.concat(binary)

    end
虽然很不要脸,但是还请您多多打赏 ^_^