Skip to content
Browse files

Added Research for parsing h264 video content (from RTP stream) with …

…Ruby.
  • Loading branch information...
1 parent 4ebc295 commit f8895c4475dbc89212a638853b291e3ae20ff990 @newfront committed Apr 6, 2012
View
46 media/video/parse_h264/ffmpeg_encoding_filters/ffmpeg_encoding_filters.rtf
@@ -0,0 +1,46 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww16800\viewh7580\viewkind0
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural
+
+\f0\b\fs24 \cf0 FFMPEG ENCODING OPTIONS
+\b0 (to libx264, high quality)\
+\
+ffmpeg -i bbb_trailer_iphone.m4v -s cif -sameq -vcodec libx264 -vpre slow -b 300k -r 24 -threads 0 -aspect 4:3 -s cif -padtop 44 -padbottom 46 -padcolor 000000 -f h264 test_to_mov_cif.mov\
+\
+ffmpeg -i bbb_trailer_iphone.m4v -vcodec libx264 -vpre slow -threads 0 -f h264 test_bbb_to_raw_for_fsv.h264\
+\
+
+\b (used)
+\b0 ffmpeg -i bbb_trailer_iphone.m4v -vcodec libx264 -vpre slow -threads 0 -b 300k -r 29 -f h264 test_bbb_to_raw_for_fsv.h264\
+\
+
+\b (stream copy)
+\b0 ffmpeg -f h264 -s cif -b 300k -r 29 -i test_bbb_to_raw_for_fsv.h264 -scodec h264 -s cif /Users/Scott/Desktop/test_copy_stream.mov\
+\
+
+\b FFMPEG RAW ELEMENTARY BITSTREAM TO RTP\
+(libavformat/rtpproto.c)\
+\
+
+\b0 ffmpeg -f h264 -s cif -b 300k -r 29 -i test_bbb_to_raw_for_fsv.h264 -scodec h264 -f rtp rtp://127.0.0.1:6000?option=pkt_size=1500&connect=1\
+\
+(libavformat/sdp.c)\
+\
+struct sdp_session_level \{\
+ int sdp_version; /**< protocol version (currently 0) */\
+ int id; /**< session ID */\
+ int version; /**< session version */\
+ int start_time; /**< session start time (NTP time, in seconds),\
+ or 0 in case of permanent session */\
+ int end_time; /**< session end time (NTP time, in seconds),\
+ or 0 if the session is not bounded */\
+ int ttl; /**< TTL, in case of multicast stream */\
+ const char *user; /**< username of the session's creator */\
+ const char *src_addr; /**< IP address of the machine from which the session was created */\
+ const char *src_type; /**< address type of src_addr */\
+ const char *dst_addr; /**< destination IP address (can be multicast) */\
+ const char *dst_type; /**< destination IP address type */\
+ const char *name; /**< session name (can be an empty string) */\
+\};}
View
BIN media/video/parse_h264/fsv/record_fsv_h264.fsv
Binary file not shown.
View
227 media/video/parse_h264/fsv_to_raw_h264.rb
@@ -0,0 +1,227 @@
+#!/usr/bin/ruby
+require Dir.getwd+'/tools/rtp_header_parser.rb'
+require Dir.getwd+'/tools/video_helpers.rb'
+
+$argv = ARGV unless ARGV.nil?
+@fsv = ''
+@hex = []
+
+def read_file_to_binary(file=nil)
+
+ if file.nil?
+ file = $argv[0]
+ end
+
+ # open file in read,binary mode, parse each byte to variable
+ @fsv = VideoHelper.open_up_file_parse_bin_to_hex(file,"rb")
+
+ # byte to hex
+ @fsv.each_byte do |byte|
+ @hex << "%02X" % byte
+ end
+
+ @fsv_header = VideoFormatTools.parse_fsv_header(@fsv[0,167])
+
+ puts "\nFSV FILE TYPE\nVersion: #{@fsv_header[:version]}\nCodec Name: #{@fsv_header[:codec_name]}\nFMTP INTO: #{@fsv_header[:rtp_fmtp_info]}\n"
+
+ # profile-level-id=42801f
+ # Packetization Mode = 0 | Single NAL Unit Mode (only Single NAL unit Frames, no STAP-As, or FU-As)
+ # level-asymmetry-allowed=1 |
+ # max-mbps=108000 | Max Macroblocks per second = 108,000
+
+ # find codec extension
+ @type = VideoFormatTools.get_codec_extension_name(@fsv_header[:codec_name])
+ puts "TYPE: #{@type}\n"
+
+ count = 0
+
+ #puts "version: #{@version.to_i(16).to_s}\ncodec_name: #{@codec_name}\nfmtp_info: #{@rtp_fmtp_info}\n"
+
+ # sync_source_id usage.
+ # {"sync_id":count}
+ # ex: {"cb7d8a90"=>32, "ffffffff"=>3}, we know that cb7d8a90 is the pattern that must be in each true rtp header since its value is higher
+
+ @sync_source_id = {}
+
+ # loop through headers, grab everything that matches criteria
+ # (there will be some misc noise in most files that sneaks in... use @sync_source_id intervals to check for the correct sync_source_id in the set)
+ for @i in 0..(@hex.size)-1 do
+ #puts "HEX PATTERN #{@hex[@i,12].join}" if (@hex[@i,12].join).match(/^80(f|7)b([0-9a-f]*)/i)
+ if @hex[@i,12].join.match(/^80(f|7)b([0-9a-f]*)/i)
+ (@rtp_header_locations ||= []) << @i
+ tmp = @hex[@i,12].join[(@hex[@i,12].join).size-8,(@hex[@i,12].join).size]
+ if @sync_source_id.has_key? tmp
+ tmp_count = @sync_source_id.fetch(tmp)+1
+ @sync_source_id.store(tmp,tmp_count)
+ else
+ @sync_source_id.store(tmp,1)
+ end
+ count += 1
+ end
+ end
+
+ # loop through headers, grab rtp headers and stuff into @rtp_headers Array, also grab last 4 bytes, and push into @sync_source_id Hash
+ #for @j in 0..(headers.size-1) do
+ #(@rtp_headers ||= []) << headers[@j] if headers[@j].match(pattern)
+ # now check the last 5 values match, and add to array
+ #tmp = headers[@j][headers[@j].size-8,headers[@j].size]
+ #if @sync_source_id.has_key? tmp
+ #tmp_count = @sync_source_id.fetch(tmp)+1
+ #@sync_source_id.store(tmp,tmp_count)
+ #else
+ #@sync_source_id.store(tmp,1)
+ #end
+ #end
+
+ puts @sync_source_id.inspect
+
+ # spit out @sync_source_id.values into @main_sync_source
+ # sort array desc order
+
+ #puts "sort by desc order, rtp header sync source id"
+ #@main_sync_source = @sync_source_id.values.sort {|x,y| y <=> x }
+
+ # find key for value in original @sync_source_id hash
+ #@sync_source_id.each {|key,value|
+ #if value == @main_sync_source[0]
+ #@real_sync_source_id = key
+ #break
+ #end
+ #}
+ #puts "TRUE SYNC SOURCE ID: #{@real_sync_source_id.to_s}"
+
+ # delete from headers array where last 4 bytes are not equal to the @real_sync_source_id
+ #for @i in 0..(headers.size-1) do
+ #(@real_rtp_headers ||= []) << headers[@i] unless headers[@i][headers[@i].size-8,headers[@i].size] != @real_sync_source_id
+ #end
+
+ # read hex, find rtp header patterns, grab H.263 RFC4629 payload
+
+ @last_rtp_header_sequence_number = 0
+ @current_rtp_header_sequence_number = 0
+ @sequence_order_correct = false
+
+ for @k in 0..(@rtp_header_locations.size-1) do
+ # show the full capture in hex from the rtp header to the position of the next rtp header
+ begin
+ previous_rtp_header = 0
+ current_rtp_header= 0
+ next_rtp_header = 0
+
+ previous_rtp_header = @rtp_header_locations[(@k-1)] if @k >= 1
+ current_rtp_header = @rtp_header_locations[@k]
+ next_rtp_header = @rtp_header_locations[(@k+1)] if @k < (@rtp_header_locations.size-1)
+ # why current_rtp_header +4, the h263 payload header has a 4 byte header length
+ if @k < (@rtp_header_locations.size-1)
+ # grab the RTP header and parse format for usable values
+ rtp_header = RTPTools.parse_rtp_header(@hex[current_rtp_header,12].join)
+
+ # grab sequence number from hex to decimal
+ @current_rtp_header_sequence_number = rtp_header[:sequence_number].to_i(16)
+
+ # Check that packets are not out of order
+ # also helps us check errors in rtp header search regexp
+ if @current_rtp_header_sequence_number > @last_rtp_header_sequence_number
+ if (@current_rtp_header_sequence_number == @last_rtp_header_sequence_number+1 ) || @last_rtp_header_sequence_number == 0
+ #puts "sequence is incrementing : #{@current_rtp_header_sequence_number.to_s} > #{@last_rtp_header_sequence_number.to_s}"
+ #puts "#{@hex[current_rtp_header,12].join}"
+ @sequence_order_correct = true
+ @last_rtp_header_sequence_number = @current_rtp_header_sequence_number
+ else
+ #puts "(BAD) #{@hex[current_rtp_header,12].join}"
+ #puts rtp_header.inspect
+ puts "sequence is out of order : #{@current_rtp_header_sequence_number.to_s} > #{@last_rtp_header_sequence_number.to_s}"
+ @sequence_order_correct = false
+ end
+ else
+ #puts "RTP HEADER\n#{@hex[current_rtp_header,12].join}"
+ #puts rtp_header.inspect
+ puts "sequence is out of order : #{@current_rtp_header_sequence_number.to_s} > #{@last_rtp_header_sequence_number.to_s}"
+ @sequence_order_correct = false
+ end
+
+ payload_data = @hex[current_rtp_header,((next_rtp_header) - (current_rtp_header))].join
+
+ # if there is extra padding, kill it
+ # /40010000(0|8|F)*/i
+
+ find_padding = payload_data.index(/40010000/)
+ if find_padding
+ payload_data = payload_data[0,find_padding]
+ end
+
+ puts "\n****************************************\n"
+ puts "h264 Payload NAL unit header\n"
+ puts payload_data[24,2].to_s
+ puts "\n****************************************\n"
+
+
+ # check the marker bit, if it is one, don't cut of trailing 8 bits
+ if rtp_header[:marker].to_i == 1
+ #payload_data starts at 26th bit, that gets rid of rtp header and h264 NAL Unit
+ # not sure if nal unit needs to go yet
+ # not sure if it is expecting added 2 zero byte bytes
+ payload_data = "000001"+payload_data[24,payload_data.bytesize-24].to_s
+ else
+ # start 26 bytes in, end 34 before end of size, 26 + 8 for 8 bytes trailing with P-Frames
+ payload_data = "000001"+payload_data[24,payload_data.bytesize-32].to_s
+ end
+
+ puts "\n-----------------------------------------------------------\n"
+ puts "Payload Data (type 1):\n#{payload_data}\nBytesize: #{payload_data.bytesize.to_s}"
+ puts "\n-----------------------------------------------------------\n"
+
+ else
+ payload_data = @hex[current_rtp_header,((@hex.length) - (current_rtp_header))].join
+ rtp_header = RTPTools.parse_rtp_header(@hex[current_rtp_header,12].join)
+
+ find_padding = payload_data.index(/40010000/)
+ if find_padding
+ payload_data = payload_data[0,find_padding]
+ end
+
+ puts "\n****************************************\n"
+ puts "h264 Payload NAL unit header\n"
+ puts payload_data[24,2].to_s
+ puts "\n****************************************\n"
+
+ if rtp_header[:marker].to_i == 1
+ #payload_data starts at 26th bit, that gets rid of rtp header and h264 NAL Unit
+ # not sure if nal unit needs to go yet
+ # not sure if it is expecting added 2 zero byte bytes
+ payload_data = "000001"+payload_data[24,payload_data.bytesize-24].to_s
+ else
+ # start 26 bytes in, end 34 before end of size, 26 + 8 for 8 bytes trailing with P-Frames
+ payload_data = "000001"+payload_data[24,payload_data.bytesize-32].to_s
+ end
+
+ puts "\n-----------------------------------------------------------\n"
+ puts "Payload Data (type 2):\n#{payload_data}\n"
+ puts "\n-----------------------------------------------------------\n"
+ end
+
+ unless payload_data.nil?
+ (@raw_h26x_hex_string ||= "") << payload_data unless !@sequence_order_correct
+ end
+ rescue
+ raise "Failed to read h263-1998 format."
+ end
+ end
+
+ # if we are here, then we have our hex only data
+ # show our collected h263-1998 data
+ #puts "\nHEX STRING FROM h263-1998 collection algorithm:\n #{@raw_h26x_hex_string}\n"
+
+ # convert hex_string back to bin_string
+ puts "saving binary string from hex string"
+ bin_video_data = VideoHelper.write_binary_from_hex_string(@raw_h26x_hex_string)
+
+ #STDOUT.write(bin_video_data) #looks good
+
+ # save bin_string to file
+ VideoHelper.save_raw_h26x($argv[0],bin_video_data,@type)
+end
+
+raise "you need to specify an FSV file in ARGV[0]. (ex) ruby fsv_to_raw_h264.rb /path/to/your/file.fsv" unless !$argv[0].nil?
+read_file_to_binary
+
View
232 media/video/parse_h264/notes/h264_packet_capture_to_raw_h264_notes.rtf
@@ -0,0 +1,232 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww17120\viewh17120\viewkind0
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural
+
+\f0\b\fs30 \cf0 h264 Packet Capture (fsv) to raw h264
+\b0 \
+------------------------------------------------------\
+\
+capture duration:
+\b 22
+\b0 seconds\
+wireshark capture file: h264_rtp_packet_capture_04_05_at_1_16.pcap
+\fs24 \
+\
+
+\b\fs30 RTP HEADERS
+\fs24 \
+
+\b0 \
+
+\fs32 01:
+\b 807b09
+\b0 f9
+\b 0b05
+\b0 f854
+\b cb7d8a90
+\b0 (NAL Unit - Sequence parameter set)
+\b \
+
+\b0 02:
+\b 807b09
+\b0 fa
+\b 0b05
+\b0 f854
+\b cb7d8a90
+\b0 (NAL Unit - Picture parameter set)
+\b \
+
+\b0 03.
+\b 80
+\b0 fb
+\b 09
+\b0 fb
+\b 0b05
+\b0 f854
+\b cb7d8a90
+\b0 (Mark NAL unit - codec slice of an IDR picture)
+\b \
+
+\b0 04.
+\b 80fb09
+\b0 fc
+\b 0b
+\b0 061474
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)
+\b \
+
+\b0 05.
+\b 80fb09
+\b0 fd
+\b 0b06
+\b0 2d10
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)
+\b \
+
+\b0 06.
+\b 80fb09
+\b0 fe
+\b 0b06
+\b0 45ac
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)
+\b \
+
+\b0 07.
+\b 80fb09
+\b0 ff
+\b 0b06
+\b0 5e48
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)
+\b \
+
+\b0 08.
+\b 80fb
+\b0 0a000
+\b b0
+\b0 68f80
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+09.
+\b 80fb
+\b0 0a010
+\b b0
+\b0 6a81c
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+10.
+\b 80fb0a
+\b0 020b06c0b8
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+11.
+\b 80fb0a
+\b0 030b06d954
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+12.
+\b 80fb0a
+\b0 040b06f1f0
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+13.
+\b 80fb0a
+\b0 050b070a8c
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+14.
+\b 80fb0a
+\b0 060b072328
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+15.
+\b 80fb0a
+\b0 070b073bc4
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+16.
+\b 80fb0a
+\b0 080b075460
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+17.
+\b 80fb0a
+\b0 090b076cfc
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+18.
+\b 80fb0a
+\b0 0a0b078598
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+19.
+\b 80fb0a
+\b0 0b0b079e34
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+20.
+\b 80fb0a
+\b0 0c0b07b6d0
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+21.
+\b 80fb0a
+\b0 0d0b07cf6c
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+22.
+\b 80fb0a
+\b0 0e0b07e808
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+23.
+\b 80fb0a
+\b0 0f0b8000a4
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+24.
+\b 80fb0a
+\b0 100b081940
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+25.
+\b 80fb0a
+\b0 110b0831dc
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+26.
+\b 80fb0a
+\b0 120b086314
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+27.
+\b 80fb0a
+\b0 130b087bb0
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+28.
+\b 80fb0a
+\b0 140b08944c
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+29.
+\b 80fb0a
+\b0 150b08c584
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+30.
+\b 80fb0a
+\b0 160b08de20
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+31.
+\b 80fb0a
+\b0 170b08f6bc
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+32.
+\b 80fb0a
+\b0 180b090f58
+\b cb7d8a90
+\b0 (Mark NAL unit - coded slice of an non-IDR picture)\
+
+\fs24 \
+
+\b\fs30 Pattern
+\b0\fs24 \
+First 5 bytes match (with variance between 7 and f for marker frames), Last 8 bytes match\
+\
+
+\b\fs30 ^80(f|7)b0([A-Fa-f0-9]\{19\})\
+\
+
+\b0\fs26 when looking back and forwards on the captured headers, ensure that the last 4 bytes (ex: "cb","7d","8a","90") match, this ensures that the rtp headers are in the same session (
+\b Synchronization Source
+\b0 Identifier match)
+\fs24 \
+}
View
96 media/video/parse_h264/notes/raw_yuv_formats_README.txt
@@ -0,0 +1,96 @@
+
+ --------------------------------------------
+ Video Quality Experts Group Test Sequences
+ --------------------------------------------
+
+This directory contains the Video Quality Experts Group (VQEG) test sequences.
+
+NOTE: Each sequence file beneath this directory is 182MB. Download
+ times of an hour per sequence is typical.
+
+The sequence files are in the format specified by the VQEG Objective Video
+Quality Model Test Plan except the alignment patterns have been removed.
+
+The format is as follows:
+
+ 10 Frames(Not used) + 8seconds Video + 10 Frames(Not used)
+
+The 10 frames of unused video allow enough frames for an MPEG2
+codec to stabilize. Objective models will skip these frames.
+
+There are two video formats: 525@60Hz and 625@50Hz. The format may be
+identified by the sequence file name. The 525@60Hz sequence file
+names end with _525.yuv and the 625@50Hz sequences end with _625.yuv.
+The 525 sequences are 260 frames long and the 625 sequences are 220 frames.
+
+Both formats contain 720 pixels (1440 bytes) per horizontal line.
+The 525 sequences have 486 active lines per frame and the 625 sequences
+have 576 active lines per frame. Each line is in multiplexed 4:2:2
+component video format as follows:
+
+ Cb Y Cr Y ...
+ 720 Y bytes per line
+ 360 Cb bytes per line
+ 360 Cr bytes per line
+
+Lines are concatenated into frames and frames are concatenated to form the
+sequence files. The frames are identical to 8bit Abekas (also called Quantel)
+files. There are no file headers.
+
+All sequences are interlaced video except:
+
+ src1 - which has no motion (all frames are identical).
+ src7 - which is a progressive sequence.
+ src13 - converted from 24Hz film by the 3/2 pull down method.
+
+For the interlaced sequences:
+
+The lines of the two fields are interlaced into the frames. The top field
+of the 525@60Hz material is temporally LATER than the bottom field (Bottom
+field first) and the top field of the 625@50Hz material is temporally EARLIER
+than the bottom field (Top field first).
+
+The frame sizes are:
+
+ 525@60Hz Frame size = 1440 x 486 = 699840 bytes/frame
+ 625@50Hz Frame size = 1440 x 576 = 829440 bytes/frame
+
+And the sequence sizes are:
+
+ 525@60Hz 8 sec + 20 frames file size = 699840 x 260 frames = 181958400 bytes
+ 625@50Hz 8 sec + 20 frames file size = 829440 x 220 frames = 182476800 bytes
+
+Directories:
+
+Reference - The Original unimpaired sequences.
+
+ThumbNails - An HTML "thumb nail" index to the reference sequences.
+
+HRC01 - HRC16 - The Hypothetical Reference Condition Sequences
+ Each HRC is an impairment of the original Reference Sequence.
+
+ALL_525 - Links to all of the 525@60 sequences.
+ 170 files (30GB)
+
+ALL_625 - Links to all of the 625@50 sequences.
+ 170 files (30GB)
+
+More Information:
+
+More information is avaliable on the official VQEG website:
+
+http://www.vqeg.org/
+
+and in the various VQEG reports in the directory:
+
+ftp://ftp.crc.ca/crc/vqeg/phase1-docs/
+
+Various tools for converting and displaying these files can be found in:
+
+ftp://ftp.crc.ca/crc/vqeg/TestSequenceTools
+ftp://vqeg.its.bldrdoc.gov/VideoTools
+
+
+
+
+Phil Blanchfield CRC
View
BIN media/video/parse_h264/notes/rfc3984-h264.pdf
Binary file not shown.
View
50 media/video/parse_h264/parse_h264_for_headers.rb
@@ -0,0 +1,50 @@
+#!/usr/bin/ruby
+
+# grab list of headers, throw in some fake headers for inconsistency
+# erroneous headers have been sprinkled in...
+headers = ["80fb0fffffffffffffffffff","807b09f90b05f854cb7d8a90","807b09fa0b05f854cb7d8a90","80fb09fb0b05f854cb7d8a90","80fb09fc0b061474cb7d8a90","80fb09fd0b062d10cb7d8a90","80fb09fe0b0645accb7d8a90","80fb09ff0b065e48cb7d8a90","80fb0a000b068f80cb7d8a90","80fb0fffffffffffffffffff","80fb0a010b06a81ccb7d8a90","80fb0a020b06c0b8cb7d8a90","80fb0a030b06d954cb7d8a90","80fb0a040b06f1f0cb7d8a90","80fb0a050b070a8ccb7d8a90","80fb0a060b072328cb7d8a90","80fb0a070b073bc4cb7d8a90","80fb0a080b075460cb7d8a90","80fb0a090b076cfccb7d8a90","80fb0a0a0b078598cb7d8a90","80fb0a0b0b079e34cb7d8a90","80fb0a0c0b07b6d0cb7d8a90","80fb0a0d0b07cf6ccb7d8a90","80fb0a0e0b07e808cb7d8a90","80fb0a0f0b8000a4cb7d8a90","80fb0a100b081940cb7d8a90","80fb0a110b0831dccb7d8a90","80fb0a120b086314cb7d8a90","80fb0a130b087bb0cb7d8a90","80fb0a140b08944ccb7d8a90","80fb0a150b08c584cb7d8a90","80fb0a160b08de20cb7d8a90","80fb0a170b08f6bccb7d8a90","80fb0a180b090f58cb7d8a90","80fb0fffffffffffffffffff"]
+
+pattern = /^80(f|7)b0([a-f0-9]{19})/i
+
+# loop through headers, grab everything that matches criteria
+# (there will be some misc noise in most files that sneaks in... use @sync_source_id intervals to check for the correct sync_source_id in the set)
+
+# sync_source_id usage.
+# {"sync_id":count}
+# ex: {"cb7d8a90"=>32, "ffffffff"=>3}, we know that cb7d8a90 is the pattern that must be in each true rtp header since its value is higher
+@sync_source_id = {}
+
+# loop through headers, grab rtp headers and stuff into @rtp_headers Array, also grab last 4 bytes, and push into @sync_source_id Hash
+for @j in 0..(headers.size-1) do
+ (@rtp_headers ||= []) << headers[@j] if headers[@j].match(pattern)
+ # now check the last 5 values match, and add to array
+ tmp = headers[@j][headers[@j].size-8,headers[@j].size]
+ if @sync_source_id.has_key? tmp
+ tmp_count = @sync_source_id.fetch(tmp)+1
+ @sync_source_id.store(tmp,tmp_count)
+ else
+ @sync_source_id.store(tmp,1)
+ end
+end
+
+puts @sync_source_id.inspect
+
+# spit out @sync_source_id.values into @main_sync_source
+# sort array desc order
+@main_sync_source = @sync_source_id.values.sort {|x,y| y <=> x }
+
+# find key for value in original @sync_source_id hash
+@sync_source_id.each {|key,value|
+ if value == @main_sync_source[0]
+ @real_sync_source_id = key
+ break
+ end
+}
+puts @real_sync_source_id.to_s
+
+# delete from headers array where last 4 bytes are not equal to the @real_sync_source_id
+for @i in 0..(headers.size-1) do
+ (@real_rtp_headers ||= []) << headers[@i] unless headers[@i][headers[@i].size-8,headers[@i].size] != @real_sync_source_id
+end
+
+@real_rtp_headers.each {|rtp_header| puts "rtp_header: #{rtp_header}"}
View
41 media/video/parse_h264/parse_h264_for_patterns.rb
@@ -0,0 +1,41 @@
+#!/usr/bin/ruby
+require Dir.getwd+'/tools/rtp_header_parser.rb'
+require Dir.getwd+'/tools/video_helpers.rb'
+
+$argv = ARGV unless ARGV.nil?
+@fsv = ''
+@hex = []
+
+def read_file_to_binary(file=nil)
+
+ if file.nil?
+ file = $argv[0]
+ end
+
+ # open file in read,binary mode, parse each byte to variable
+ @fsv = VideoHelper.open_up_file_parse_bin_to_hex(file,"rb")
+
+ # byte to hex
+ @fsv.each_byte do |byte|
+ @hex << "%02X" % byte
+ end
+
+ count = 0
+
+ pattern = /^00008([a-f0-9])/i
+
+ for @i in 0..(@hex.size)-1 do
+ if @hex[@i,3].join.match(pattern)
+ (@rtp_header_locations ||= []) << @i
+ puts "FOUND Sequence #{@hex[@i,4].join}"
+ count += 1
+ end
+ end
+
+ return
+
+end
+
+raise "you need to specify an recorded stream file (pcap, stream file) in ARGV[0]. (ex) ruby fsv_to_raw_h264.rb /path/to/your/file.fsv" unless !$argv[0].nil?
+read_file_to_binary
+
View
BIN media/video/parse_h264/pcap/h264_rtp_packets_04_05_at_1_16.pcap
Binary file not shown.
View
BIN media/video/parse_h264/record_fsv_h264.fsv
Binary file not shown.
View
BIN media/video/parse_h264/record_fsv_h264.stream
Binary file not shown.
View
59 media/video/parse_h264/tools/hex_to_bin_converter.rb
@@ -0,0 +1,59 @@
+#!/usr/bin/ruby
+
+module NumberFormat
+
+# hex lookup table
+@hex_table = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
+
+# binary lookup table
+@bin_table = ["0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"]
+
+def NumberFormat.hex_or_bin_conversion(hex_array, direction="to_bin")
+
+ raise "Error. First parameter must be filled in" unless !hex_array.nil?
+
+ unless hex_array.is_a? Array
+ hex_array = hex_array.lines.to_a
+ end
+
+ tmp = ''
+ # if the hex array is flattened, unflatten as hex pairs
+ if hex_array.length == 1
+ bin = []
+ hex_array.to_s.scan(/../).map do |match|
+ bin << match
+ end
+ hex_array = bin
+ end
+
+ begin
+ hex_array.each do |hex|
+ begin
+ for @i in 0..(@hex_table.size-1) do
+ if hex[0,1].to_s.upcase == @hex_table[@i]
+ tmp << @bin_table[@i]
+ break
+ end
+ end
+ rescue
+ puts"first hex to bin chunk conversion failed"
+ end
+
+ begin
+ for @k in 0..(@hex_table.size-1) do
+ if hex[1,1].to_s.upcase == @hex_table[@k]
+ tmp << @bin_table[@k]
+ break
+ end
+ end
+ rescue
+ puts"second hex to bin chunk conversion failed"
+ end
+ end
+ return tmp
+ rescue
+ puts "failed to convert hex to binary"
+ end
+end
+
+end
View
0 media/video/parse_h264/tools/parse_h263-1998_payload_header.rb
No changes.
View
87 media/video/parse_h264/tools/rtp_header_parser.rb
@@ -0,0 +1,87 @@
+#!/usr/bin/ruby
+require (Dir.getwd)+'/tools/hex_to_bin_converter.rb'
+# parse RTP headers
+# start with hex, or binary
+
+module RTPTools
+ @rtp_string_formats = ["hex","bin"]
+ # note
+ # h264 is a dynamic payload type
+ @payload_types = {"h264"=>123,"h263-1998"=>115,"h263"=>34}
+ def RTPTools.parse_rtp_header(rtp_header_string,format="hex")
+ rtp_header_string = rtp_header_string.lines.to_a if rtp_header_string.is_a? String
+
+ raise "Error. you need to specify the parsing format. eg. 'hex' or 'bin'" unless @rtp_string_formats.include?(format)
+ raise "Error. you need to pass a hex or binary string into the parser parameter 1" unless rtp_header_string.is_a? Array
+
+ # only hex for now, but can use hex_to_bin
+ bin_string = NumberFormat.hex_or_bin_conversion(rtp_header_string,"to_bin")
+ # now that we have bin_string back, we can iterate through it to read off the header
+
+ # read string format into hash
+ rtp_header = {}
+ # Version, Padding, Extension, Contributing Sources (8 bits)
+ rtp_header[:version] = bin_string[0,2] # 2 bit version
+ rtp_header[:padding] = bin_string[2,1] # 1 bit padding
+ rtp_header[:extension] = bin_string[3,1] # 1 bit eXtension
+ rtp_header[:contributing_sources] = bin_string[4,4] # 4 bits Contributing Sources
+
+ # Marker, and Payload Type (8 bits)
+ rtp_header[:marker] = bin_string[8,1] # 1 bit marker bit
+ rtp_header[:payload_type] = rtp_header_string[0][2,2]
+ # Sequence Number (16 bits)
+ # doesn't have to start at 0, in fact it is frowned upon to start at zero, in the case of RTP session hijacking
+ rtp_header[:sequence_number] = rtp_header_string[0][4,4]
+
+ # Timestamp (32 bits)
+ # doesn't have to start at 0, in fact it is frowned upon to start at zero, in the case of RTP session hijacking
+ rtp_header[:timestamp] = rtp_header_string[0][6,4] #timestamp 32 bit integer
+
+ # Synchronization Source (SSRC) identifer
+ # this is the unique session identifier for this RTP session
+ rtp_header[:ssrc_id] = rtp_header_string[0][10,4] # synchronization source (SSRC) identifier
+
+
+ # find human readable format, so we can see what is going on
+ rtp_header_human_readable = RTPTools.parse_rtp_header_containers_to_human_readable_formats(rtp_header)
+
+ #puts "\n"
+ rtp_header_human_readable.each {|key,value| puts "#{key} : #{value}\n"}
+ #puts "\n"
+
+ return rtp_header
+ end
+
+ # parse the rtp header container values into human readable format
+ def RTPTools.parse_rtp_header_containers_to_human_readable_formats(rtp_header_hash)
+ raise "Error. You must send over a hash in the parameter" unless rtp_header_hash.is_a? Hash
+
+ begin
+ # version: 2 bits (convert to hex)
+ rtp_headers_human_readable = {}
+ rtp_header_hash[:version].lines.to_a.pack('H*') == "\020" ? rtp_headers_human_readable[:version] = 2 : rtp_headers_human_readable[:version] = 1
+ rtp_header_hash[:padding].lines.to_a.pack('H*') == "\000" ? rtp_headers_human_readable[:padding] = 0 : rtp_headers_human_readable[:padding] = 1
+ rtp_header_hash[:extension].lines.to_a.pack('H*') == "\000" ? rtp_headers_human_readable[:extension] = 0 : rtp_headers_human_readable[:extension] = 1
+ rtp_header_hash[:contributing_sources].lines.to_a.pack('H*') == "\000\000" ? rtp_headers_human_readable[:contributing_sources] = 0 : rtp_headers_human_readable[:contributing_sources] = 1
+ rtp_header_hash[:marker].lines.to_a.pack('H*') == "\020" ? rtp_headers_human_readable[:marker] = 1 : rtp_headers_human_readable[:marker] = 0
+
+ @payload_types.each do |key,value|
+ if value == rtp_header_hash[:payload_type].to_s.to_i(16)
+ rtp_headers_human_readable[:payload_type] = key
+ break
+ end
+ end
+
+ rtp_headers_human_readable[:sequence_number] = rtp_header_hash[:sequence_number].to_s.to_i(16)
+ rtp_headers_human_readable[:timestamp] = rtp_header_hash[:timestamp].to_i(16)
+ rtp_headers_human_readable[:ssrc_id] = rtp_header_hash[:ssrc_id].to_i(16)
+ return rtp_headers_human_readable
+ rescue
+ raise "Error. Conversion from Binary to Human Legable Format Failed"
+ end
+ end
+
+end
+
+# Usage
+RTPTools.parse_rtp_header("80731C3B689F91E8443B89B2")
View
103 media/video/parse_h264/tools/video_helpers.rb
@@ -0,0 +1,103 @@
+#!/usr/bin/ruby
+require (Dir.getwd)+"/tools/hex_to_bin_converter.rb"
+
+module VideoHelper
+
+ def VideoHelper.write_binary_from_hex_string(hex_string)
+ raise "Error. Missing hex_string in parameter one. " unless !hex_string.nil?
+ return hex_string.lines.to_a.pack('H*')
+ end
+
+
+ # opens up a file, and reads binary
+ # saves binary string to variable fsv
+ # returns fsv
+ def VideoHelper.open_up_file_parse_bin_to_hex(file,read_type)
+ # open up fsv file
+ fsv = ''
+ tmp = ''
+ File.open(file,read_type) do |f|
+ # grab each bit and push into array
+ while !f.eof?
+ # read in larger chunks, fairly fast
+ fsv << f.read(1024)
+ end
+ end
+ puts "FILE SIZE: #{(fsv.length)/1024} kb"
+ return fsv
+ end
+
+ # Opens up a File, writes bin data and closes file
+ # input_name = "test_movie.fsv"
+ # binary_data = binary_data_string (use VideoHelper.write_binary_from_hex_string to get binary string)
+ # type = h263,h263p,h264
+
+ def VideoHelper.save_raw_h26x(input_name,binary_data,type)
+ begin
+ save_file_name = input_name.to_s.gsub(".fsv","."+type)
+
+ puts "save to #{save_file_name}\n"
+ File.open(save_file_name,"wb") do |f|
+ f.write(binary_data)
+ end
+ return true
+ rescue
+ raise "Error saving raw h26x video data."
+ end
+ end
+
+ # used to find similarity between strings
+ def VideoHelper.compute_sum_of_squares(value1,value2)
+ sum = (value2 - value1)**2
+ return 1/(1+Math.sqrt(sum))
+ end
+
+ # expects payload header as hex
+ # ex. "0400"
+ def VideoHelper.parseh263_1998_payload_header(payload_header)
+ # hex to true bin
+ raise "Error. payload header must be hex String" unless payload_header.is_a? String
+ hex_as_bin = NumberFormat.hex_or_bin_conversion(payload_header,"to_bin")
+ puts "hex_as_bin #{hex_as_bin}"
+ end
+
+end
+
+
+
+module VideoFormatTools
+
+ # FSV FILE HEADER PARSER
+ # usage, submit first 165 bytes of FSV file, return hash of header values
+ def VideoFormatTools.parse_fsv_header(header)
+ raise "Error. You need to pass a 167 byte string as the only parameter." unless header.bytesize == 167
+ raise "Error. You need to pass a String as the header parameter." unless header.is_a? String
+
+ begin
+ # iterate through header
+ fsv_header = {}
+ fsv_header[:version] = header[0,4] # version 4 bits
+ fsv_header[:codec_name] = header[4,32] # codec name 32 bits
+ fsv_header[:rtp_fmtp_info] = header[36,128] # fmtp info 128 bits
+ fsv_header[:rtp_audio_rate] = header[164,1].to_i(16) # audio rate 40 = 64
+ fsv_header[:rtp_audio_ptime] = header[165,1].to_i(16) # audio payload time
+ fsv_header[:rtp_timestamp] = header[166,1].to_i(16) # switch_time_t created
+ return fsv_header
+ rescue
+ raise "Error. Failed to parse FSV header."
+ end
+ end
+
+ # grab the real codec extension name
+ def VideoFormatTools.get_codec_extension_name(codec_code)
+
+ if codec_code.match(/h263-(1998|2000)/i)
+ return "h263p"
+ elsif codec_code.match(/h263/i)
+ return "h263"
+ elsif codec_code.match(/h264/i)
+ return "h264"
+ end
+ end
+
+end

0 comments on commit f8895c4

Please sign in to comment.
Something went wrong with that request. Please try again.