Wireshark Lua: 一個從RTP抓包裏導出H.264 Payload,變成264裸碼流文件(xxx.264)的Wireshark插件

    抓取一個包含H.264 Payload RTP包的SIP會話或RTSP會話後,用Wireshark的Play功能只能播放聲音,不能播放視頻。把RTP payload直接導出成文件後也是不能直接播放的,因爲H.264 over RTP封包是符合RFC3984規範的,必須按照該規範把H.264數據取出來後,組成NALU,放到avi/mp4或裸碼流文件等容器裏後才能播放。

     本人寫了一個wireshark插件,可以在打開包含H.264碼流的抓包後,選菜單“Tools->Export H264 to file [HQX's plugins]”後,把抓包文件裏的H.264碼流自動導出到抓包文件所在目錄(工作目錄)裏,名爲from_<RTP流源ip>_<RTP流源端口>_to_<RTP流目的ip>_<RTP流目的端口>.264的264裸碼流文件裏。(文件格式爲每個NALU前加0x00000001分隔符)。

      本程序可以識別RFC3984裏提到的三種H.264 over RTP封裝,分別是Single NALU(一個RTP含一個NALU)、STAP-A(一個RTP包含多個NALU)、FU-A(一個NALU分佈到多個RTP包)三種封裝格式,且會自動把SPS和PPS放到裸碼流文件頭部。

Lua腳本如下:

[python] view plain copy
  1. -- Dump RTP h.264 payload to raw h.264 file (*.264)  
  2. -- According to RFC3984 to dissector H264 payload of RTP to NALU, and write it  
  3. -- to from<sourceIp_sourcePort>to<dstIp_dstPort>.264 file. By now, we support single NALU,  
  4. -- STAP-A and FU-A format RTP payload for H.264.  
  5. -- You can access this feature by menu "Tools->Export H264 to file [HQX's plugins]"  
  6. -- Author: Huang Qiangxiong ([email protected])  
  7. -- change log:  
  8. --      2012-03-13  
  9. --          Just can play  
  10. ------------------------------------------------------------------------------------------------  
  11. do  
  12.     -- for geting h264 data (the field's value is type of ByteArray)  
  13.     local f_h264 = Field.new("h264")   
  14.   
  15.     -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this function  
  16.     local function export_h264_to_file()  
  17.         -- window for showing information  
  18.         local tw = TextWindow.new("Export H264 to File Info Win")  
  19.         local pgtw = ProgDlg.new("Export H264 to File Process""Dumping H264 data to file...")  
  20.           
  21.         -- add message to information window  
  22.         function twappend(str)  
  23.             tw:append(str)  
  24.             tw:append("\n")  
  25.         end  
  26.           
  27.         -- running first time for counting and finding sps+pps, second time for real saving  
  28.         local first_run = true   
  29.         -- variable for storing rtp stream and dumping parameters  
  30.         local stream_infos = {}  
  31.   
  32.         -- trigered by all h264 packats  
  33.         local my_h264_tap = Listener.new(tap, "h264")  
  34.           
  35.         -- get rtp stream info by src and dst address  
  36.         function get_stream_info(pinfo)  
  37.             local key = "from_" .. tostring(pinfo.src) .. "_" .. tostring(pinfo.src_port) .. "to" .. tostring(pinfo.dst) .. "_" .. tostring(pinfo.dst_port)  
  38.             local stream_info = stream_infos[key]  
  39.             if not stream_info then -- if not exists, create one  
  40.                 stream_info = { }  
  41.                 stream_info.filename = key.. ".264"  
  42.                 stream_info.file = io.open(stream_info.filename, "wb")  
  43.                 stream_info.counter = 0 -- counting h264 total NALUs  
  44.                 stream_info.counter2 = 0 -- for second time running  
  45.                 stream_infos[key] = stream_info  
  46.                 twappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port)   
  47.                          .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n         [" .. stream_info.filename .. "] ...\n")  
  48.             end  
  49.             return stream_info  
  50.         end  
  51.           
  52.         -- write a NALU or part of NALU to file.  
  53.         function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)  
  54.             if first_run then  
  55.                 stream_info.counter = stream_info.counter + 1  
  56.                   
  57.                 if begin_with_nalu_hdr then  
  58.                     -- save SPS or PPS  
  59.                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)  
  60.                     if not stream_info.sps and nalu_type == 7 then  
  61.                         stream_info.sps = str_bytes  
  62.                     elseif not stream_info.pps and nalu_type == 8 then  
  63.                         stream_info.pps = str_bytes  
  64.                     end  
  65.                 end  
  66.                   
  67.             else -- second time running  
  68.                 if stream_info.counter2 == 0 then  
  69.                     -- write SPS and PPS to file header first  
  70.                     if stream_info.sps then  
  71.                         stream_info.file:write("\00\00\00\01")  
  72.                         stream_info.file:write(stream_info.sps)  
  73.                     else  
  74.                         twappend("Not found SPS for [" .. stream_info.filename .. "], it might not be played!\n")  
  75.                     end  
  76.                     if stream_info.pps then  
  77.                         stream_info.file:write("\00\00\00\01")  
  78.                         stream_info.file:write(stream_info.pps)  
  79.                     else  
  80.                         twappend("Not found PPS for [" .. stream_info.filename .. "], it might not be played!\n")  
  81.                     end  
  82.                 end  
  83.               
  84.                 if begin_with_nalu_hdr then  
  85.                     -- *.264 raw file format seams that every nalu start with 0x00000001  
  86.                     stream_info.file:write("\00\00\00\01")  
  87.                 end  
  88.                 stream_info.file:write(str_bytes)  
  89.                 stream_info.counter2 = stream_info.counter2 + 1  
  90.                   
  91.                 if stream_info.counter2 == stream_info.counter then  
  92.                     stream_info.file:flush()  
  93.                     twappend("File [" .. stream_info.filename .. "] generated OK!\n")  
  94.                 end  
  95.                 -- update progress window's progress bar  
  96.                 if stream_info.counter > 0 then pgtw:update(stream_info.counter2 / stream_info.counter) end  
  97.             end  
  98.         end  
  99.           
  100.         -- read RFC3984 about single nalu/stap-a/fu-a H264 payload format of rtp  
  101.         -- single NALU: one rtp payload contains only NALU  
  102.         function process_single_nalu(stream_info, h264)  
  103.             write_to_file(stream_info, h264:tvb()():string(), true)  
  104.         end  
  105.           
  106.         -- STAP-A: one rtp payload contains more than one NALUs  
  107.         function process_stap_a(stream_info, h264)  
  108.             local h264tvb = h264:tvb()  
  109.             local offset = 1  
  110.             repeat  
  111.                 local size = h264tvb(offset,2):uint()  
  112.                 write_to_file(stream_info, h264tvb(offset+2, size):string(), true)  
  113.                 offset = offset + 2 + size  
  114.             until offset >= h264tvb:len()  
  115.         end  
  116.           
  117.         -- FU-A: one rtp payload contains only one part of a NALU (might be begin, middle and end part of a NALU)  
  118.         function process_fu_a(stream_info, h264)  
  119.             local h264tvb = h264:tvb()  
  120.             local fu_idr = h264:get_index(0)  
  121.             local fu_hdr = h264:get_index(1)  
  122.             if bit.band(fu_hdr, 0x80) ~= 0 then  
  123.                 -- start bit is set then save nalu header and body  
  124.                 local nalu_hdr = bit.bor(bit.band(fu_idr, 0xE0), bit.band(fu_hdr, 0x1F))  
  125.                 write_to_file(stream_info, string.char(nalu_hdr), true)  
  126.             else  
  127.                 -- start bit not set, just write part of nalu body  
  128.             end  
  129.             write_to_file(stream_info, h264tvb(2):string(), false)  
  130.         end  
  131.           
  132.         -- call this function if a packet contains h264 payload  
  133.         function my_h264_tap.packet(pinfo,tvb)  
  134.             local h264s = { f_h264() } -- using table because one packet may contains more than one RTP  
  135.             for i,h264_f in ipairs(h264s) do  
  136.                 if h264_f.len < 2 then  
  137.                     return  
  138.                 end  
  139.                 local h264 = h264_f.value   -- is ByteArray  
  140.                 local hdr_type = bit.band(h264:get_index(0), 0x1F)  
  141.                 local stream_info = get_stream_info(pinfo)  
  142.                   
  143.                 if hdr_type > 0 and hdr_type < 24 then  
  144.                     -- Single NALU  
  145.                     process_single_nalu(stream_info, h264)  
  146.                 elseif hdr_type == 24 then  
  147.                     -- STAP-A Single-time aggregation  
  148.                     process_stap_a(stream_info, h264)  
  149.                 elseif hdr_type == 28 then  
  150.                     -- FU-A  
  151.                     process_fu_a(stream_info, h264)  
  152.                 else  
  153.                     twappend("Error: unknown type=" .. hdr_type .. " ; we only know 1-23(Single NALU),24(STAP-A),28(FU-A)!")  
  154.                 end  
  155.             end  
  156.         end  
  157.           
  158.         -- close all open files  
  159.         function close_all_files()  
  160.             if stream_infos then  
  161.                 for id,stream in pairs(stream_infos) do  
  162.                     if stream and stream.file then  
  163.                         stream.file:close()  
  164.                         stream.file = nil  
  165.                     end  
  166.                 end  
  167.             end  
  168.         end  
  169.           
  170.         function my_h264_tap.reset()  
  171.             -- do nothing now  
  172.         end  
  173.           
  174.         function remove()  
  175.             close_all_files()  
  176.             my_h264_tap:remove()  
  177.         end  
  178.           
  179.         tw:set_atclose(remove)  
  180.           
  181.         -- first time it runs for counting h.264 packets and finding SPS and PPS  
  182.         retap_packets()  
  183.         first_run = false  
  184.         -- second time it runs for saving h264 data to target file.  
  185.         retap_packets()  
  186.         -- close progress window  
  187.         pgtw:close()  
  188.     end  
  189.       
  190.     -- Find this feature in menu "Tools->"Export H264 to file [HQX's plugins]""  
  191.     register_menu("Export H264 to file [HQX's plugins]", export_h264_to_file, MENU_TOOLS_UNSORTED)  
  192. end  


把代碼保存成h264_export.lua文件,放到wireshark安裝目錄下,然後修改wireshark安裝目錄下的init.lua文件:

(1)若有disable_lua = true這樣的行,則註釋掉;

(2)在文件末加入dofile(DATA_DIR.."h264_export.lua")

重新打開wirekshark就能使用該功能了。

 

另外,264裸碼流文件一般播放器不一定能播放,推薦使用ffmpeg的ffplay播放,或用ffmpeg轉成通用文件格式播放。

 

2014年升級版,支持排序、丟棄不完整幀,注意生成的文件from...在抓拍文件相同的目錄:

[python] view plain copy
  1. -- Dump RTP h.264 payload to raw h.264 file (*.264)  
  2. -- According to RFC3984 to dissector H264 payload of RTP to NALU, and write it  
  3. -- to from<sourceIp_sourcePort>to<dstIp_dstPort>.264 file. By now, we support single NALU,  
  4. -- STAP-A and FU-A format RTP payload for H.264.  
  5. -- You can access this feature by menu "Tools->Export H264 to file [HQX's plugins]"  
  6. -- Author: Huang Qiangxiong ([email protected])  
  7. -- change log:  
  8. --      2012-03-13  
  9. --          Just can play  
  10. --      2012-04-28  
  11. --          Add local to local function, and add [local bit = require("bit")] to prevent  
  12. --          bit recleared in previous file.  
  13. --      2013-07-11  
  14. --          Add sort RTP and drop uncompleted frame option.  
  15. --      2013-07-19  
  16. --          Do nothing when tap is triggered other than button event.  
  17. --          Add check for first or last packs lost of one frame.  
  18. ------------------------------------------------------------------------------------------------  
  19. do  
  20.     local bit = require("bit")  
  21.   
  22.     -- for geting h264 data (the field's value is type of ByteArray)  
  23.     local f_h264 = Field.new("h264")   
  24.     local f_rtp = Field.new("rtp")   
  25.     local f_rtp_seq = Field.new("rtp.seq")  
  26.     local f_rtp_timestamp = Field.new("rtp.timestamp")  
  27.     local nalu_type_list = {  
  28.         [0] = "Unspecified",  
  29.         [1] = "P/B_slice",  
  30.         [2] = "P/B_A",  
  31.         [3] = "P/B_B",  
  32.         [4] = "P/B_C",  
  33.         [5] = "I_slice",  
  34.         [6] = "SEI",  
  35.         [7] = "SPS",  
  36.         [8] = "PPS",  
  37.         [9] = "AUD",  
  38.     }  
  39.       
  40.     local function get_enum_name(list, index)  
  41.         local value = list[index]  
  42.         return value and value or "Unknown"  
  43.     end  
  44.   
  45.     -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this function  
  46.     local function export_h264_to_file()  
  47.         -- window for showing information  
  48.         local tw = TextWindow.new("Export H264 to File Info Win")  
  49.         --local pgtw = ProgDlg.new("Export H264 to File Process""Dumping H264 data to file...")  
  50.         local pgtw;  
  51.           
  52.         -- add message to information window  
  53.         function twappend(str)  
  54.             tw:append(str)  
  55.             tw:append("\n")  
  56.         end  
  57.           
  58.         -- running first time for counting and finding sps+pps, second time for real saving  
  59.         local first_run = true   
  60.         -- variable for storing rtp stream and dumping parameters  
  61.         local stream_infos = nil  
  62.         -- drop_uncompleted_frame  
  63.         local drop_uncompleted_frame = false  
  64.         -- max frame buffer size  
  65.         local MAX_FRAME_NUM = 3  
  66.   
  67.         -- trigered by all h264 packats  
  68.         local my_h264_tap = Listener.new(tap, "h264")  
  69.           
  70.         -- get rtp stream info by src and dst address  
  71.         function get_stream_info(pinfo)  
  72.             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")  
  73.             local stream_info = stream_infos[key]  
  74.             if not stream_info then -- if not exists, create one  
  75.                 stream_info = { }  
  76.                 stream_info.filename = key.. ".264"  
  77.                 stream_info.file = io.open(stream_info.filename, "wb")  
  78.                 stream_info.counter = 0 -- counting h264 total NALUs  
  79.                 stream_info.counter2 = 0 -- for second time running  
  80.                 stream_infos[key] = stream_info  
  81.                 twappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port)   
  82.                          .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n         [" .. stream_info.filename .. "] ...\n")  
  83.             end  
  84.             return stream_info  
  85.         end  
  86.           
  87.         -- write a NALU or part of NALU to file.  
  88.         local function real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)  
  89.             if first_run then  
  90.                 stream_info.counter = stream_info.counter + 1  
  91.                   
  92.                 if begin_with_nalu_hdr then  
  93.                     -- save SPS or PPS  
  94.                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)  
  95.                     if not stream_info.sps and nalu_type == 7 then  
  96.                         stream_info.sps = str_bytes  
  97.                     elseif not stream_info.pps and nalu_type == 8 then  
  98.                         stream_info.pps = str_bytes  
  99.                     end  
  100.                 end  
  101.                   
  102.             else -- second time running  
  103.                 --[[  
  104.                 if begin_with_nalu_hdr then  
  105.                     -- drop AUD  
  106.                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)  
  107.                     if nalu_type == 9 then  
  108.                         return;  
  109.                     end  
  110.                 end  
  111.                 ]]  
  112.                   
  113.                 if stream_info.counter2 == 0 then  
  114.                     -- write SPS and PPS to file header first  
  115.                     if stream_info.sps then  
  116.                         stream_info.file:write("\00\00\00\01")  
  117.                         stream_info.file:write(stream_info.sps)  
  118.                     else  
  119.                         twappend("Not found SPS for [" .. stream_info.filename .. "], it might not be played!\n")  
  120.                     end  
  121.                     if stream_info.pps then  
  122.                         stream_info.file:write("\00\00\00\01")  
  123.                         stream_info.file:write(stream_info.pps)  
  124.                     else  
  125.                         twappend("Not found PPS for [" .. stream_info.filename .. "], it might not be played!\n")  
  126.                     end  
  127.                 end  
  128.               
  129.                 if begin_with_nalu_hdr then  
  130.                     -- *.264 raw file format seams that every nalu start with 0x00000001  
  131.                     stream_info.file:write("\00\00\00\01")  
  132.                 end  
  133.                 stream_info.file:write(str_bytes)  
  134.                 stream_info.counter2 = stream_info.counter2 + 1  
  135.   
  136.                 -- update progress window's progress bar  
  137.                 if stream_info.counter > 0 and stream_info.counter2 < stream_info.counter then pgtw:update(stream_info.counter2 / stream_info.counter) end  
  138.             end  
  139.         end  
  140.           
  141.         local function comp_pack(p1, p2)  
  142.             if math.abs(p2.seq - p1.seq) < 1000 then  
  143.                 return p1.seq < p2.seq  
  144.             else -- seqeunce is over 2^16, so the small one is much big  
  145.                 return p1.seq > p2.seq  
  146.             end  
  147.         end  
  148.           
  149.         local function print_seq_error(stream_info, str)  
  150.             if stream_info.seq_error_counter == nil then  
  151.                 stream_info.seq_error_counter = 0  
  152.             end  
  153.             stream_info.seq_error_counter = stream_info.seq_error_counter + 1  
  154.             twappend(str .. " SeqErrCounts=" .. stream_info.seq_error_counter)  
  155.         end  
  156.           
  157.         local function sort_and_write(stream_info, frame)  
  158.             table.sort(frame.packs, comp_pack)  
  159.               
  160.             -- check if it is uncompleted frame  
  161.             local completed = true  
  162.             for i = 1#frame.packs - 1, 1 do  
  163.                 local seq1 = frame.packs[i].seq  
  164.                 local seq2 = frame.packs[i+1].seq  
  165.                 if bit.band(seq1+10xFFFF) ~= seq2 then  
  166.                     print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq between " .. seq1 .. " and " .. seq2)  
  167.                     completed = false  
  168.                 end  
  169.             end  
  170.               
  171.             if not frame.packs[1].nalu_begin then  
  172.                 print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq before " .. frame.packs[1].seq)  
  173.                 completed = false  
  174.             end  
  175.               
  176.             if not frame.packs[#frame.packs].nalu_end then  
  177.                 print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq after " .. frame.packs[#frame.packs].seq)  
  178.                 completed = false  
  179.             end  
  180.               
  181.             if completed then  
  182.                 for i = 1#frame.packs, 1 do  
  183.                     real_write_to_file(stream_info, frame.packs[i].data, frame.packs[i].nalu_begin)  
  184.                 end  
  185.             else  
  186.                 twappend("   We drop one uncompleted frame: rtp.timestamp=" .. frame.timestamp   
  187.                          .. " nalu_type=" .. frame.nalu_type .."(" .. get_enum_name(nalu_type_list, frame.nalu_type) .. ")")  
  188.             end  
  189.         end  
  190.           
  191.         local function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr, timestamp, seq, end_of_nalu)  
  192.             if drop_uncompleted_frame and not first_run then -- sort and drop uncompleted frame  
  193.                 if stream_info.frame_buffer_size == nil then  
  194.                     stream_info.frame_buffer_size = 0  
  195.                 end  
  196.                   
  197.                 if timestamp < 0 or seq < 0 then  
  198.                     twappend(" Invalid rtp timestamp (".. timestamp .. ") or seq (".. seq .. ")! We have to write it to file directly!")  
  199.                     real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)  
  200.                     return;  
  201.                 end  
  202.                   
  203.                 -- check if this frame has existed  
  204.                 local p = stream_info.frame_buffer  
  205.                 while p do  
  206.                     if p.timestamp == timestamp then  
  207.                         break;  
  208.                     else  
  209.                         p = p.next  
  210.                     end  
  211.                 end  
  212.                   
  213.                 if p then  -- add this pack to frame  
  214.                     if begin_with_nalu_hdr then  
  215.                         p.nalu_type = bit.band(str_bytes:byte(1), 0x1F)  
  216.                     end  
  217.                     table.insert(p.packs, { ["seq"] = seq, ["data"] = str_bytes , ["nalu_begin"] = begin_with_nalu_hdr, ["nalu_end"] = end_of_nalu })  
  218.                     return  
  219.                 end  
  220.                   
  221.                 if stream_info.frame_buffer_size >= MAX_FRAME_NUM then  
  222.                     -- write the most early frame to file  
  223.                     sort_and_write(stream_info, stream_info.frame_buffer)  
  224.                     stream_info.frame_buffer = stream_info.frame_buffer.next  
  225.                     stream_info.frame_buffer_size = stream_info.frame_buffer_size - 1  
  226.                 end  
  227.                   
  228.                 -- create a new frame buffer for new frame (timestamp)  
  229.                 local frame = {}  
  230.                 frame.timestamp = timestamp  
  231.                 if begin_with_nalu_hdr then  
  232.                     frame.nalu_type = bit.band(str_bytes:byte(1), 0x1F)  
  233.                 end  
  234.                 frame.packs = {{ ["seq"] = seq, ["data"] = str_bytes, ["nalu_begin"] = begin_with_nalu_hdr, ["nalu_end"] = end_of_nalu}}  -- put pack to index 1 pos  
  235.                 frame.next = nil  
  236.                   
  237.                 if stream_info.frame_buffer_size == 0 then  -- first frame  
  238.                     stream_info.frame_buffer = frame  
  239.                 else  
  240.                     p = stream_info.frame_buffer  
  241.                     while p.next do  
  242.                         p = p.next  
  243.                     end  
  244.                     p.next = frame  
  245.                 end  
  246.                 stream_info.frame_buffer_size = stream_info.frame_buffer_size + 1  
  247.                   
  248.             else -- write data direct to file without sort or frame drop  
  249.                 real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)  
  250.             end  
  251.         end  
  252.           
  253.         -- read RFC3984 about single nalu/stap-a/fu-a H264 payload format of rtp  
  254.         -- single NALU: one rtp payload contains only NALU  
  255.         local function process_single_nalu(stream_info, h264, timestamp, seq)  
  256.             write_to_file(stream_info, h264:tvb()():string(), true, timestamp, seq, true)  
  257.         end  
  258.           
  259.         -- STAP-A: one rtp payload contains more than one NALUs  
  260.         local function process_stap_a(stream_info, h264, timestamp, seq)  
  261.             local h264tvb = h264:tvb()  
  262.             local offset = 1  
  263.             local i = 1  
  264.             repeat  
  265.                 local size = h264tvb(offset,2):uint()  
  266.                 write_to_file(stream_info, h264tvb(offset+2, size):string(), true, timestamp, i, true)  
  267.                 offset = offset + 2 + size  
  268.                 i = i + 1  
  269.             until offset >= h264tvb:len()  
  270.         end  
  271.           
  272.         -- FU-A: one rtp payload contains only one part of a NALU (might be begin, middle and end part of a NALU)  
  273.         local function process_fu_a(stream_info, h264, timestamp, seq)  
  274.             local h264tvb = h264:tvb()  
  275.             local fu_idr = h264:get_index(0)  
  276.             local fu_hdr = h264:get_index(1)  
  277.             local end_of_nalu =  (bit.band(fu_hdr, 0x40) ~= 0)  
  278.             if bit.band(fu_hdr, 0x80) ~= 0 then  
  279.                 -- start bit is set then save nalu header and body  
  280.                 local nalu_hdr = bit.bor(bit.band(fu_idr, 0xE0), bit.band(fu_hdr, 0x1F))  
  281.                 write_to_file(stream_info, string.char(nalu_hdr) .. h264tvb(2):string(), true, timestamp, seq, end_of_nalu)  
  282.             else  
  283.                 -- start bit not set, just write part of nalu body  
  284.                 write_to_file(stream_info, h264tvb(2):string(), false, timestamp, seq, end_of_nalu)  
  285.             end  
  286.         end  
  287.           
  288.         -- call this function if a packet contains h264 payload  
  289.         function my_h264_tap.packet(pinfo,tvb)  
  290.             if stream_infos == nil then  
  291.                 -- not triggered by button event, so do nothing.  
  292.                 return  
  293.             end  
  294.             local h264s = { f_h264() } -- using table because one packet may contains more than one RTP  
  295.             local rtps = { f_rtp() }  
  296.             local rtp_seqs = { f_rtp_seq() }  
  297.             local rtp_timestamps = { f_rtp_timestamp() }  
  298.               
  299.             for i,h264_f in ipairs(h264s) do  
  300.                 if h264_f.len < 2 then  
  301.                     return  
  302.                 end  
  303.                 local h264 = h264_f.value   -- is ByteArray  
  304.                 local hdr_type = bit.band(h264:get_index(0), 0x1F)  
  305.                 local stream_info = get_stream_info(pinfo)  
  306.                   
  307.                 -- search the RTP timestamp and sequence of this H264  
  308.                 local timestamp = -1  
  309.                 local seq = -1  
  310.                 if drop_uncompleted_frame then  
  311.                     for j,rtp_f in ipairs(rtps) do  
  312.                         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  
  313.                             seq = rtp_seqs[j].value  
  314.                             timestamp = rtp_timestamps[j].value  
  315.                 break  
  316.                         end  
  317.                     end  
  318.   
  319.                 end  
  320.                   
  321.                 if hdr_type > 0 and hdr_type < 24 then  
  322.                     -- Single NALU  
  323.                     process_single_nalu(stream_info, h264, timestamp, seq)  
  324.                 elseif hdr_type == 24 then  
  325.                     -- STAP-A Single-time aggregation  
  326.                     process_stap_a(stream_info, h264, timestamp, seq)  
  327.                 elseif hdr_type == 28 then  
  328.                     -- FU-A  
  329.                     process_fu_a(stream_info, h264, timestamp, seq)  
  330.                 else  
  331.                     twappend("Error: unknown type=" .. hdr_type .. " ; we only know 1-23(Single NALU),24(STAP-A),28(FU-A)!")  
  332.                 end  
  333.             end  
  334.         end  
  335.           
  336.         -- close all open files  
  337.         local function close_all_files()  
  338.             if stream_infos then  
  339.                 local no_streams = true  
  340.                 for id,stream in pairs(stream_infos) do  
  341.                     if stream and stream.file then  
  342.                         if stream.frame_buffer then  
  343.                             local p = stream.frame_buffer  
  344.                             while p do  
  345.                                 sort_and_write(stream, p)  
  346.                                 p = p.next  
  347.                             end  
  348.                             stream.frame_buffer = nil  
  349.                             stream.frame_buffer_size = 0  
  350.                         end  
  351.                         stream.file:flush()  
  352.                         stream.file:close()  
  353.                         twappend("File [" .. stream.filename .. "] generated OK!\n")  
  354.                         stream.file = nil  
  355.                         no_streams = false  
  356.                     end  
  357.                 end  
  358.                   
  359.                 if no_streams then  
  360.                     twappend("Not found any H.264 over RTP streams!")  
  361.                 end  
  362.             end  
  363.         end  
  364.           
  365.         function my_h264_tap.reset()  
  366.             -- do nothing now  
  367.         end  
  368.           
  369.         local function remove()  
  370.             my_h264_tap:remove()  
  371.         end  
  372.           
  373.         tw:set_atclose(remove)  
  374.           
  375.         local function export_h264(drop_frame)  
  376.             pgtw = ProgDlg.new("Export H264 to File Process""Dumping H264 data to file...")  
  377.             first_run = true  
  378.             drop_uncompleted_frame = drop_frame  
  379.             stream_infos = {}  
  380.             -- first time it runs for counting h.264 packets and finding SPS and PPS  
  381.             retap_packets()  
  382.             first_run = false  
  383.             -- second time it runs for saving h264 data to target file.  
  384.             retap_packets()  
  385.             close_all_files()  
  386.             -- close progress window  
  387.             pgtw:close()  
  388.             stream_infos = nil  
  389.         end  
  390.           
  391.         local function export_all()  
  392.             export_h264(false)  
  393.         end  
  394.           
  395.         local function export_completed_frames()  
  396.             export_h264(true)  
  397.         end  
  398.           
  399.         tw:add_button("Export All", export_all)  
  400.         tw:add_button("Export Completed Frames (Drop uncompleted frames)", export_completed_frames)  
  401.     end  
  402.       
  403.     -- Find this feature in menu "Tools->"Export H264 to file [HQX's plugins]""  
  404.     register_menu("Export H264 to file [HQX's plugins]", export_h264_to_file, MENU_TOOLS_UNSORTED)  
  405. end  

2015年升級版:

[python] view plain copy
  1. -- Dump RTP h.264 payload to raw h.264 file (*.264)  
  2. -- According to RFC3984 to dissector H264 payload of RTP to NALU, and write it  
  3. -- to from<sourceIp_sourcePort>to<dstIp_dstPort>.264 file. By now, we support single NALU,  
  4. -- STAP-A and FU-A format RTP payload for H.264.  
  5. -- You can access this feature by menu "Tools->Export H264 to file [HQX's plugins]"  
  6. -- Author: Huang Qiangxiong ([email protected])  
  7. -- change log:  
  8. --      2012-03-13  
  9. --          Just can play  
  10. --      2012-04-28  
  11. --          Add local to local function, and add [local bit = require("bit")] to prevent  
  12. --          bit recleared in previous file.  
  13. --      2013-07-11  
  14. --          Add sort RTP and drop uncompleted frame option.  
  15. --      2013-07-19  
  16. --          Do nothing when tap is triggered other than button event.  
  17. --          Add check for first or last packs lost of one frame.  
  18. --      2014-10-23  
  19. --          Fixed bug about print a frame.nalu_type error.  
  20. --      2014-11-07  
  21. --          Add support for Lua 5.2(>1.10.1and 5.1(<=1.10.1).   
  22. --          Change range:string() to range:raw().  
  23. --          Change h264_f.value to h264_f.range:bytes() because of wireshark lua bug.  
  24. --      2015-06-03  
  25. --          Fixed bug that if ipv6 address is using the file will not generated.(we replace ':' to '.')  
  26. ------------------------------------------------------------------------------------------------  
  27. do  
  28.     --local bit = require("bit") -- only work before 1.10.1  
  29.     --local bit = require("bit32") -- only work after 1.10.1 (only support in Lua 5.2)  
  30.     local version_str = string.match(_VERSION, "%d+[.]%d*")  
  31.     local version_num = version_str and tonumber(version_str) or 5.1  
  32.     local bit = (version_num >= 5.2and require("bit32"or require("bit")  
  33.   
  34.     -- for geting h264 data (the field's value is type of ByteArray)  
  35.     local f_h264 = Field.new("h264")   
  36.     local f_rtp = Field.new("rtp")   
  37.     local f_rtp_seq = Field.new("rtp.seq")  
  38.     local f_rtp_timestamp = Field.new("rtp.timestamp")  
  39.     local nalu_type_list = {  
  40.         [0] = "Unspecified",  
  41.         [1] = "P/B_slice",  
  42.         [2] = "P/B_A",  
  43.         [3] = "P/B_B",  
  44.         [4] = "P/B_C",  
  45.         [5] = "I_slice",  
  46.         [6] = "SEI",  
  47.         [7] = "SPS",  
  48.         [8] = "PPS",  
  49.         [9] = "AUD",  
  50.     }  
  51.       
  52.     local function get_enum_name(list, index)  
  53.         local value = list[index]  
  54.         return value and value or "Unknown"  
  55.     end  
  56.   
  57.     -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this function  
  58.     local function export_h264_to_file()  
  59.         -- window for showing information  
  60.         local tw = TextWindow.new("Export H264 to File Info Win")  
  61.         --local pgtw = ProgDlg.new("Export H264 to File Process""Dumping H264 data to file...")  
  62.         local pgtw;  
  63.           
  64.         -- add message to information window  
  65.         function twappend(str)  
  66.             tw:append(str)  
  67.             tw:append("\n")  
  68.         end  
  69.           
  70.         -- running first time for counting and finding sps+pps, second time for real saving  
  71.         local first_run = true   
  72.         -- variable for storing rtp stream and dumping parameters  
  73.         local stream_infos = nil  
  74.         -- drop_uncompleted_frame  
  75.         local drop_uncompleted_frame = false  
  76.         -- max frame buffer size  
  77.         local MAX_FRAME_NUM = 3  
  78.   
  79.         -- trigered by all h264 packats  
  80.         local my_h264_tap = Listener.new(tap, "h264")  
  81.           
  82.         -- get rtp stream info by src and dst address  
  83.         function get_stream_info(pinfo)  
  84.             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")  
  85.             key = key:gsub(":"".")  
  86.             local stream_info = stream_infos[key]  
  87.             if not stream_info then -- if not exists, create one  
  88.                 stream_info = { }  
  89.                 stream_info.filename = key.. ".264"  
  90.                 stream_info.file = io.open(stream_info.filename, "wb")  
  91.                 stream_info.counter = 0 -- counting h264 total NALUs  
  92.                 stream_info.counter2 = 0 -- for second time running  
  93.                 stream_infos[key] = stream_info  
  94.                 twappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port)   
  95.                          .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n         [" .. stream_info.filename .. "] ...\n")  
  96.             end  
  97.             return stream_info  
  98.         end  
  99.           
  100.         -- write a NALU or part of NALU to file.  
  101.         local function real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)  
  102.             if first_run then  
  103.                 stream_info.counter = stream_info.counter + 1  
  104.                   
  105.                 if begin_with_nalu_hdr then  
  106.                     -- save SPS or PPS  
  107.                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)  
  108.                     if not stream_info.sps and nalu_type == 7 then  
  109.                         stream_info.sps = str_bytes  
  110.                     elseif not stream_info.pps and nalu_type == 8 then  
  111.                         stream_info.pps = str_bytes  
  112.                     end  
  113.                 end  
  114.                   
  115.             else -- second time running  
  116.                 --[[  
  117.                 if begin_with_nalu_hdr then  
  118.                     -- drop AUD  
  119.                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)  
  120.                     if nalu_type == 9 then  
  121.                         return;  
  122.                     end  
  123.                 end  
  124.                 ]]  
  125.                   
  126.                 if stream_info.counter2 == 0 then  
  127.                     -- write SPS and PPS to file header first  
  128.                     if stream_info.sps then  
  129.                         stream_info.file:write("\00\00\00\01")  
  130.                         stream_info.file:write(stream_info.sps)  
  131.                     else  
  132.                         twappend("Not found SPS for [" .. stream_info.filename .. "], it might not be played!\n")  
  133.                     end  
  134.                     if stream_info.pps then  
  135.                         stream_info.file:write("\00\00\00\01")  
  136.                         stream_info.file:write(stream_info.pps)  
  137.                     else  
  138.                         twappend("Not found PPS for [" .. stream_info.filename .. "], it might not be played!\n")  
  139.                     end  
  140.                 end  
  141.               
  142.                 if begin_with_nalu_hdr then  
  143.                     -- *.264 raw file format seams that every nalu start with 0x00000001  
  144.                     stream_info.file:write("\00\00\00\01")  
  145.                 end  
  146.                 stream_info.file:write(str_bytes)  
  147.                 stream_info.counter2 = stream_info.counter2 + 1  
  148.   
  149.                 -- update progress window's progress bar  
  150.                 if stream_info.counter > 0 and stream_info.counter2 < stream_info.counter then pgtw:update(stream_info.counter2 / stream_info.counter) end  
  151.             end  
  152.         end  
  153.           
  154.         local function comp_pack(p1, p2)  
  155.             if math.abs(p2.seq - p1.seq) < 1000 then  
  156.                 return p1.seq < p2.seq  
  157.             else -- seqeunce is over 2^16, so the small one is much big  
  158.                 return p1.seq > p2.seq  
  159.             end  
  160.         end  
  161.           
  162.         local function print_seq_error(stream_info, str)  
  163.             if stream_info.seq_error_counter == nil then  
  164.                 stream_info.seq_error_counter = 0  
  165.             end  
  166.             stream_info.seq_error_counter = stream_info.seq_error_counter + 1  
  167.             twappend(str .. " SeqErrCounts=" .. stream_info.seq_error_counter)  
  168.         end  
  169.           
  170.         local function sort_and_write(stream_info, frame)  
  171.             table.sort(frame.packs, comp_pack)  
  172.               
  173.             -- check if it is uncompleted frame  
  174.             local completed = true  
  175.             for i = 1#frame.packs - 1, 1 do  
  176.                 local seq1 = frame.packs[i].seq  
  177.                 local seq2 = frame.packs[i+1].seq  
  178.                 if bit.band(seq1+10xFFFF) ~= seq2 then  
  179.                     print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq between " .. seq1 .. " and " .. seq2)  
  180.                     completed = false  
  181.                 end  
  182.             end  
  183.               
  184.             if not frame.packs[1].nalu_begin then  
  185.                 print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq before " .. frame.packs[1].seq)  
  186.                 completed = false  
  187.             end  
  188.               
  189.             if not frame.packs[#frame.packs].nalu_end then  
  190.                 print_seq_error(stream_info, " RTP pack Lost: timestamp=" .. frame.timestamp .. " seq after " .. frame.packs[#frame.packs].seq)  
  191.                 completed = false  
  192.             end  
  193.               
  194.             if completed then  
  195.                 for i = 1#frame.packs, 1 do  
  196.                     real_write_to_file(stream_info, frame.packs[i].data, frame.packs[i].nalu_begin)  
  197.                 end  
  198.             else  
  199.                 twappend("   We drop one uncompleted frame: rtp.timestamp=" .. frame.timestamp   
  200.                          .. " nalu_type=" .. (frame.nalu_type and frame.nalu_type .."(" .. get_enum_name(nalu_type_list, frame.nalu_type) .. ")" or "unknown") )  
  201.             end  
  202.         end  
  203.           
  204.         local function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr, timestamp, seq, end_of_nalu)  
  205.             if drop_uncompleted_frame and not first_run then -- sort and drop uncompleted frame  
  206.                 if stream_info.frame_buffer_size == nil then  
  207.                     stream_info.frame_buffer_size = 0  
  208.                 end  
  209.                   
  210.                 if timestamp < 0 or seq < 0 then  
  211.                     twappend(" Invalid rtp timestamp (".. timestamp .. ") or seq (".. seq .. ")! We have to write it to file directly!")  
  212.                     real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)  
  213.                     return;  
  214.                 end  
  215.                   
  216.                 -- check if this frame has existed  
  217.                 local p = stream_info.frame_buffer  
  218.                 while p do  
  219.                     if p.timestamp == timestamp then  
  220.                         break;  
  221.                     else  
  222.                         p = p.next  
  223.                     end  
  224.                 end  
  225.                   
  226.                 if p then  -- add this pack to frame  
  227.                     if begin_with_nalu_hdr then  
  228.                         p.nalu_type = bit.band(str_bytes:byte(1), 0x1F)  
  229.                     end  
  230.                     table.insert(p.packs, { ["seq"] = seq, ["data"] = str_bytes , ["nalu_begin"] = begin_with_nalu_hdr, ["nalu_end"] = end_of_nalu })  
  231.                     return  
  232.                 end  
  233.                   
  234.                 if stream_info.frame_buffer_size >= MAX_FRAME_NUM then  
  235.                     -- write the most early frame to file  
  236.                     sort_and_write(stream_info, stream_info.frame_buffer)  
  237.                     stream_info.frame_buffer = stream_info.frame_buffer.next  
  238.                     stream_info.frame_buffer_size = stream_info.frame_buffer_size - 1  
  239.                 end  
  240.                   
  241.                 -- create a new frame buffer for new frame (timestamp)  
  242.                 local frame = {}  
  243.                 frame.timestamp = timestamp  
  244.                 if begin_with_nalu_hdr then  
  245.                     frame.nalu_type = bit.band(str_bytes:byte(1), 0x1F)  
  246.                 end  
  247.                 frame.packs = {{ ["seq"] = seq, ["data"] = str_bytes, ["nalu_begin"] = begin_with_nalu_hdr, ["nalu_end"] = end_of_nalu}}  -- put pack to index 1 pos  
  248.                 frame.next = nil  
  249.                   
  250.                 if stream_info.frame_buffer_size == 0 then  -- first frame  
  251.                     stream_info.frame_buffer = frame  
  252.                 else  
  253.                     p = stream_info.frame_buffer  
  254.                     while p.next do  
  255.                         p = p.next  
  256.                     end  
  257.                     p.next = frame  
  258.                 end  
  259.                 stream_info.frame_buffer_size = stream_info.frame_buffer_size + 1  
  260.                   
  261.             else -- write data direct to file without sort or frame drop  
  262.                 real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)  
  263.             end  
  264.         end  
  265.           
  266.         -- read RFC3984 about single nalu/stap-a/fu-a H264 payload format of rtp  
  267.         -- single NALU: one rtp payload contains only NALU  
  268.         local function process_single_nalu(stream_info, h264, timestamp, seq)  
  269.             --write_to_file(stream_info, h264:tvb()():string(), true, timestamp, seq, true)  
  270.             write_to_file(stream_info, ((version_num >= 5.2and h264:tvb():raw() or h264:tvb()():string()), true, timestamp, seq, true)  
  271.         end  
  272.           
  273.         -- STAP-A: one rtp payload contains more than one NALUs  
  274.         local function process_stap_a(stream_info, h264, timestamp, seq)  
  275.             local h264tvb = h264:tvb()  
  276.             local offset = 1  
  277.             local i = 1  
  278.             repeat  
  279.                 local size = h264tvb(offset,2):uint()  
  280.                 --write_to_file(stream_info, h264tvb(offset+2, size):string(), true, timestamp, i, true)  
  281.                 write_to_file(stream_info, ((version_num >= 5.2and h264tvb:raw(offset+2, size) or h264tvb(offset+2, size):string()), true, timestamp, i, true)  
  282.                 offset = offset + 2 + size  
  283.                 i = i + 1  
  284.             until offset >= h264tvb:len()  
  285.         end  
  286.           
  287.         -- FU-A: one rtp payload contains only one part of a NALU (might be begin, middle and end part of a NALU)  
  288.         local function process_fu_a(stream_info, h264, timestamp, seq)  
  289.             local h264tvb = h264:tvb()  
  290.             local fu_idr = h264:get_index(0)  
  291.             local fu_hdr = h264:get_index(1)  
  292.             local end_of_nalu =  (bit.band(fu_hdr, 0x40) ~= 0)  
  293.             if bit.band(fu_hdr, 0x80) ~= 0 then  
  294.                 -- start bit is set then save nalu header and body  
  295.                 local nalu_hdr = bit.bor(bit.band(fu_idr, 0xE0), bit.band(fu_hdr, 0x1F))  
  296.                 --write_to_file(stream_info, string.char(nalu_hdr) .. h264tvb(2):string(), true, timestamp, seq, end_of_nalu)  
  297.                 write_to_file(stream_info, string.char(nalu_hdr) .. ((version_num >= 5.2and h264tvb:raw(2or h264tvb(2):string()), true, timestamp, seq, end_of_nalu)  
  298.             else  
  299.                 -- start bit not set, just write part of nalu body  
  300.                 --write_to_file(stream_info, h264tvb(2):string(), false, timestamp, seq, end_of_nalu)  
  301.                 write_to_file(stream_info, ((version_num >= 5.2and h264tvb:raw(2or h264tvb(2):string()), false, timestamp, seq, end_of_nalu)  
  302.             end  
  303.         end  
  304.           
  305.         -- call this function if a packet contains h264 payload  
  306.         function my_h264_tap.packet(pinfo,tvb)  
  307.             if stream_infos == nil then  
  308.                 -- not triggered by button event, so do nothing.  
  309.                 return  
  310.             end  
  311.             local h264s = { f_h264() } -- using table because one packet may contains more than one RTP  
  312.             local rtps = { f_rtp() }  
  313.             local rtp_seqs = { f_rtp_seq() }  
  314.             local rtp_timestamps = { f_rtp_timestamp() }  
  315.               
  316.             for i,h264_f in ipairs(h264s) do  
  317.                 if h264_f.len < 2 then  
  318.                     return  
  319.                 end  
  320.                 --local h264 = h264_f.value   -- is ByteArray, it only works for 1.10.1 or early version  
  321.                 --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  
  322.                 local h264 = (version_num >= 5.2and h264_f.range:bytes() or h264_f.value   
  323.                 local hdr_type = bit.band(h264:get_index(0), 0x1F)  
  324.                 local stream_info = get_stream_info(pinfo)  
  325. --twappend(string.format("hdr_type=%X %d", hdr_type, hdr_type))   
  326. --twappend("bytearray=" .. tostring(h264))  
  327. --twappend("byterange=" .. tostring(h264_f.range):upper())  
  328.                 -- search the RTP timestamp and sequence of this H264  
  329.                 local timestamp = -1  
  330.                 local seq = -1  
  331.                 -- debug begin  
  332.                 local rtplen = -1  
  333.                 local preh264_foffset = -1  
  334.                 local prertp_foffset = -1  
  335.                 local preh264len = -1  
  336.                 -- debug end  
  337.                 if drop_uncompleted_frame then  
  338.                     local matchx = 0;  
  339.                     for j,rtp_f in ipairs(rtps) do  
  340.                         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  
  341.                         -- debug begin  
  342.                         --if h264_f.offset > rtp_f.offset and h264_f.offset < rtp_f.offset+rtp_f.len then  
  343.                     matchx = matchx + 1  
  344.                     if matchx > 1 then  
  345.                         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)  
  346.                     end        
  347.                     -- debug end  
  348.                             seq = rtp_seqs[j].value  
  349.                             timestamp = rtp_timestamps[j].value  
  350.                             -- debug begin  
  351.                             rtplen = rtp_f.len  
  352.                             preh264_foffset = h264_f.offset  
  353.                             prertp_foffset = rtp_f.offset  
  354.                             preh264len = h264_f.len  
  355.                             -- debug end  
  356.                             break  
  357.                         end  
  358.                     end  
  359.   
  360.                 end  
  361.                   
  362.                 if hdr_type > 0 and hdr_type < 24 then  
  363.                     -- Single NALU  
  364.                     process_single_nalu(stream_info, h264, timestamp, seq)  
  365.                 elseif hdr_type == 24 then  
  366.                     -- STAP-A Single-time aggregation  
  367.                     process_stap_a(stream_info, h264, timestamp, seq)  
  368.                 elseif hdr_type == 28 then  
  369.                     -- FU-A  
  370.                     process_fu_a(stream_info, h264, timestamp, seq)  
  371.                 else  
  372.                     twappend("Error: unknown type=" .. hdr_type .. " ; we only know 1-23(Single NALU),24(STAP-A),28(FU-A)!")  
  373.                 end  
  374.             end  
  375.         end  
  376.           
  377.         -- close all open files  
  378.         local function close_all_files()  
  379.             if stream_infos then  
  380.                 local no_streams = true  
  381.                 for id,stream in pairs(stream_infos) do  
  382.                     if stream and stream.file then  
  383.                         if stream.frame_buffer then  
  384.                             local p = stream.frame_buffer  
  385.                             while p do  
  386.                                 sort_and_write(stream, p)  
  387.                                 p = p.next  
  388.                             end  
  389.                             stream.frame_buffer = nil  
  390.                             stream.frame_buffer_size = 0  
  391.                         end  
  392.                         stream.file:flush()  
  393.                         stream.file:close()  
  394.                         twappend("File [" .. stream.filename .. "] generated OK!\n")  
  395.                         stream.file = nil  
  396.                         no_streams = false  
  397.                     end  
  398.                 end  
  399.                   
  400.                 if no_streams then  
  401.                     twappend("Not found any H.264 over RTP streams!")  
  402.                 end  
  403.             end  
  404.         end  
  405.           
  406.         function my_h264_tap.reset()  
  407.             -- do nothing now  
  408.         end  
  409.           
  410.         local function remove()  
  411.             my_h264_tap:remove()  
  412.         end  
  413.           
  414.         tw:set_atclose(remove)  
  415.           
  416.         local function export_h264(drop_frame)  
  417.             pgtw = ProgDlg.new("Export H264 to File Process""Dumping H264 data to file...")  
  418.             first_run = true  
  419.             drop_uncompleted_frame = drop_frame  
  420.             stream_infos = {}  
  421.             -- first time it runs for counting h.264 packets and finding SPS and PPS  
  422.             retap_packets()  
  423.             first_run = false  
  424.             -- second time it runs for saving h264 data to target file.  
  425.             retap_packets()  
  426.             close_all_files()  
  427.             -- close progress window  
  428.             pgtw:close()  
  429.             stream_infos = nil  
  430.         end  
  431.           
  432.         local function export_all()  
  433.             export_h264(false)  
  434.         end  
  435.           
  436.         local function export_completed_frames()  
  437.             export_h264(true)  
  438.         end  
  439.           
  440.         tw:add_button("Export All", export_all)  
  441.         tw:add_button("Export Completed Frames (Drop uncompleted frames)", export_completed_frames)  
  442.     end  
  443.       
  444.     -- Find this feature in menu "Tools->"Export H264 to file [HQX's plugins]""  
  445.     register_menu("Export H264 to file [HQX's plugins]", export_h264_to_file, MENU_TOOLS_UNSORTED)  
  446. end  
發佈了65 篇原創文章 · 獲贊 34 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章