Video Compression

VideoNerd

Content:

  1. Removal Specific SEI Message from H264/AVC Elementary Stream
  2.  Get H264/AVC Video Statistics from Transport Stream
  3.  Get Picture Statistics From HEVC/H.264 Elementary Stream
  4.  Extract a Selected GOP starting at CRA in HEVC/H.265 Elementary Stream
  5.  Print High-Level Syntax Elements (SPS/PPS) in HEVC/H.265 Elementary Stream
  6.  Modify Aspect Ratio Parameters in HEVC/H.265 Elementary Stream
  7.  Detect Discontinuities in frame_num cadence in AVC/H.264 elementary stream
  8.  Removal of Non-Reference Frames from H.264/AVC Elementary Stream in order to Reduce Bitrate
  9.  Enhancement of  Random Access Ability of H.264/AVC Stream by Insertion of SPS/PPS at IDR frames
  10.  Remove Frames Until First Key Frame in H.264/AVC File
  11.  Get DTS Statistics of both Video and Audio in MPEG2 System (Transport) Stream
  12.  Get H265/HEVC Video Statistics from Transport Stream
  13.  Remove AUDs (Access Unit Delimiters) from H.264/AVC and H.265/HEVC Elementary Stream
  14.   Print pic_parameter_set_id of each frame in H.264/AVC stream, relevant if multiple PPS are present

   15.  Print weighted prediction parameters per frame/slice (for H.264/AVC) streams,  adapted for Python 3.x

   16.  Parse SPS and Print Resolution (H.264/AVC), adapted for Python 3.x

    17.  Get Number Slices in H.264/HEVC Streams, adapted for Python 3.x

    18.   Parse SPS and Print Custom Scaling Matrices (if present) in H.264/AVC, adapted for Python 3.x

    19.   Compare SPS in H264/AVC stream, adapted for Python 3.x

20.   Get Instant Bitrates from H264 File, adapted for Python 3.x

21.   Get Instant Bitrates from HEVC File, adapted for Python 3.x

22. Remove Specific SEI Messages from HEVC File, Python 3.x

23.  Get Meta Data of HEVC Stream in MP4-file, Python 3.x

24.  Detect Large Frames in H264/AVC Elementary Stream, Python 3.x

25. Detect Large Frames in HEVC Elementary Stream, Python 3.x

26.  Find Video Track Number in mp4-file, Python 3.x

27.  Print Mpegts Packets Payload Sizes,  Python 3.x

28.   Cut H264/AVC Elementary Stream at Specified IDR,  Python 3.x

29.   Count Short Start-Codes,  Python 3.x

30.   Parse PNG Image Format, Python 3.x

31.   Parse Slice Header and Print Weighted Prediction Parameters H264, Python 3.x

32.  Get PSNR, SSIM and VIFP with sewar package, Python 3.x

33.   Print NAL Headers of HEVC Elementary Stream, Python 3.x

34.  Remove NALU with Given nal_unit_type from H264 Elementary Stream,  Python 3.x

*Additional python scripts to analyze MPEG-System stream (Transport Stream) are housed at the following page Python Scripts to Analyze and Process Transport Stream

Part of scripts written in Python 2.x

 

 

Removal Specific SEI Message from H264/AVC Elementary Stream

The script RemoveSeiInH264Stream.py  tailored to remove SEI messages specified by sei_type (e.g. sei_type=0 is buffering period SEI message).

Note: with ffmpeg you can remove all SEI NALUs regardless to sei_type:

ffmpeg -i test.mp4 -c:v copy  -c:a copy   -bsf:v ‘filter_units=remove_types=6’   test_nosei.mp4
reminder: nal-type of SEI message is 6 in h264

 

The removal process of the python script  is not in-place therefore the output stream must be different from the input one.

If the input stream does not contain a specified sei_type then no output stream is generated. Notice that most of SEI messages carry auxiliary information and can be removed from the stream (although the appearence of video might be affected in some cases).

Example [ removal of SEI messages with sei_type=0 ]

  • The input stream does not contain SEI message with sei_type=5

python RemoveSeiInH264Stream.py out.h264  5  out1.h264

Analyzing …

||==============================================================================> |||

No SEI with sei_type 5 found, output stream is not generated
  • The input stream contains SEI message with sei_type=0

python RemoveSeiInH264Stream.py  test.h264  0  out.h264

Analyzing …
||==============================================================================> |||
SEIs to remove 373
Copying without SEI 0….

This script can be useful to adapt a video stream to be playable by Microsoft Media Player (since SEI bueffering period prior to SPS makes the stream not playable.

 


 

Get H264/AVC Video Statistics from Transport Stream

The script   GetAVCVideoStatsFromTS.py   (adapted for python 2.x) and

GetAVCVideoStatsFromTSVer3.py (adapted for python 3.x)

is tailored to pick statistics (frame size, dts/pts. frame duration etc.) from video AVC/H.264 stream comprised in Mpeg System container ( Transport format files usually ending with ‘.ts’). It’s worth mentioning that the frame start is detected if a video ts-packet contains the PES header with the timestamp DTS (to avoid counting of frame slices).

Usage:

 -i           input ts-file, a section of ts-stream can be provided (even without SPS/PPS, unlike to ‘ffmpeg’)

 -v          print pts, dts and sizes of each video frame (default false)

 -a          ignore AUD (Access Unit Delimiter) violations (AUD is mandatory in Transport Stream but sometimes AUDs are absent and most of players copes with that violation),   default  false

  -p          print progress  bar (the length is 80), default false. If verbose is ON then the progress bar disappears. 

Examples:

[Print Number of detected frames, if AUD is absent a corresponding error reported  ]

python GetAVCVideoStatsFromTS.py -i test.ts
Number of Frames        900
Video Size in bytes     17415224
[Print Frame Statistics ignoring AUD violations  ]
python GetAVCVideoStatsFromTS.py -i test.ts -a -v

0   Size  50711, dts  126000,   pts  126000
1   Size  4517, dts  129600,   pts  129600, frame duration 40.00
2   Size  8079, dts  132600,   pts  132600, frame duration 33.33
3   Size  3801, dts  135600,   pts  135600, frame duration 33.33
4   Size  7159, dts  138600,   pts  138600, frame duration 33.33
5   Size  10218, dts  141600,   pts  141600, frame duration 33.33
6   Size  13865, dts  144600,   pts  144600, frame duration 33.33
7   Size  18646, dts  147600,   pts  147600, frame duration 33.33
8   Size  8108, dts  150600,   pts  150600, frame duration 33.33
9   Size  25734, dts  153600,   pts  153600, frame duration 33.33
10   Size  20030, dts  156600,   pts  156600, frame duration 33.33
…..
896   Size  19537, dts  2814573,   pts  2814573, frame duration 33.33
897   Size  23225, dts  2817573,   pts  2817573, frame duration 33.33
898   Size  1622, dts  2820573,   pts  2820573, frame duration 33.33
899   Size  28580, dts  2823573,   pts  2823573, frame duration 33.33
Minimal DTS diff (in ms)  33.32, attained at frame 17
Minimal PTS diff (in ms)  33.32, attained at frame 17
Maximal DTS diff (in ms)  40.00, attained at frame 0
Number of Frames        900
Video Size in bytes     17415224

Get Picture Statistics From HEVC/H.264 Elementary Stream

HEVC Case:

The scripts GetPictureStatsHevc.py (adapted to python 2.x) and GetPictureStatsHevcVer3.py (adapted to Python 3.x)

gathers and prints frame starts and offsets (in hex) as well as frame types.

Usage:

 -i    input hevc file
-n  maximal number of frames to process, if 0 then the whole file processed, (default 0)
Example [ print 10 frames]
          python GetPictureStatsHevc.py  -i test.h265 -n 10
gathering statistics …
idr                        0            f8
pb                        f8            3f
pb                       137            38
pb                       16f            38
pb                       1a7           4b0
pb                       657           28d
pb                       8e4           18c
pb                       a70           201
pb                       c71           dd2
pb                      1a43             0
The first column is frame type: IDR, BLA, RASL, RADL or ‘pb’ (meaning regular frame of type TRAIL).
The second column is frame start offset in hex.
The third column is frame size (in hex). An attentive reader might notice that the size of the last frame is 0. It’s not a bug, in order to get the size of the 10th frame we need process 11 frames but we constraint the script to proceed only 10 frames.

H264 Case:

The script GetPictureStatsH264.py

gathers and prints frame starts and offsets (in hex) as well as frame types of h264 elementary stream.

Usage:

 -i    input h264 file
-n    maximal number of frames to process, if 0 then the whole file processed, (default 0)
Example [print 10 frames]

python GetPictureStatsH264.py -i crowd_qsv.h264 -n 10

idr 0                8e1ad
pb 8e1ad        53618
pb e17c5        1a2d4
pb fba99        eb02
pb 10a59b     e82d
pb 118dc8     2e4bd
pb 147285    d33e
pb 1545c3    69b3
pb 15af76     7015
pb 161f8b      // if ‘-n’ is applied then the last frame size is omitted
The first column is frame type: ‘idr’ or ‘pb’ (meaning P, B or non-IDR  I-frames).
The second column is frame start offset in hex.
The third column is frame size (in hex).

Extract a Selected GOP starting at CRA in HEVC/H.265 Elementary Stream

The script CutOpenGopAtCRA.py

looks for CRA-frame started GOP and extracts it to a specified file by discarding RASL frames.

Usage:

 -i   input hevc file
-o  output hevc gop file
-g  number (counted from zero) of GOP started with CRA to extract (RASL removed) to a specified file
Example:
python CutOpenGopAtCRA.py  -i test.h265 -g 0 -o gop.h265
cra     1325c9    24875
rasl    156e3e    2eb  discarded
rasl    157129    273  discarded
rasl    15739c    22e  discarded
rasl    1575ca    164  discarded
pb      15772e    187c
pb      158faa    1b4
pb      15915e    10d
pb      15926b    155
pb      1593c0    130
pb      1594f0    24fc
pb      15b9ec    1a0
pb      15bb8c    f8
pb      15bc84    d3
pb      15bd57    1f7b
pb      15dcd2    129
pb      15ddfb    d6
pb      15ded1    1eb6
pb      15fd87    de
pb      15fe65    b0
pb      15ff15    2152
pb      162067    d5
pb      16213c    b1
pb      1621ed    20b3
pb      1642a0    114
pb      1643b4    c7
pb      16447b    d3
pb      16454e    b7
pb      164605    139c
pb      1659a1    135
pb      165ad6    d2
pb      165ba8    a7
pb      165c4f    f49
pb      166b98    18a
pb      166d22    174
pb      166e96    146
pb      166fdc    f8
pb      1670d4    155
pb      167229    7b
————————————————————————————————————————————————-

Print High-Level Syntax Elements (SPS/PPS) in HEVC/H.265 Elementary Stream

The script ParseHevcHL.py

processes HEVC/H.265 stream on GOP-basis (where SPS is an indication of GOP start, i.e. IDR without SPS is not recognized as the new GOP bebinning).

SPS and PPS (VPS is not processed) are parsed and most of syntax elements are printed.

Usage:

-i     input elementary hevc fifo or file
-n    number gops to process, if 0 then the whole stream (default 0)
Example [the first gop is processed]:
python ParseHevcHL.py -i test.h265  -n 1
SPS
sps_video_parameter_set_id         0
sps_max_sub_layers                 1
sps_temporal_id_nesting_flag       1
general_profile_space              0
general_tier_flag                  0
general_profile_idc                1
general_progressive_source_flag    1
general_interlaced_source_flag     0
general_non_packed_constraint_flag 0
general_frame_only_constraint_flag 1
general_level_idc                  123
sps_seq_parameter_set_id           0
chroma_format_idc                  1
pic_width_in_luma_samples          1920
pic_height_in_luma_samples         1080
conformance_window_flag            0
bit_depth_luma                     8
bit_depth_chroma                   8
log2_max_pic_order_cnt_lsb         8
sps_sub_layer_ordering_info        1
sps_max_dec_pic_buffering          5
sps_num_reorder_pics               2
sps_max_latency_increase_plus1     5
log2_min_coding_block_size         3
log2_diff_max_min_coding_block_size 3
log2_min_transform_block_size      2
log2_diff_max_min_transform_block_size 3
max_transform_hierarchy_depth_inter 0
max_transform_hierarchy_depth_intra 0
scaling_list_enabled_flag          0
amp_enabled_flag                   0
sample_adaptive_offset_enabled_flag 1
pcm_enabled_flag                   0
num_short_term_ref_pic_sets        0
long_term_ref_pics_present_flag    0
sps_temporal_mvp_enable_flag       1
sps_strong_intra_smoothing_enable_flag 1
vui_parameters_present_flag        1
VUI
aspect_ratio_info_present_flag     1
aspect_ratio_idc               1
overscan_info_present_flag         0
video_signal_type_present_flag     0
chroma_loc_info_present_flag       0
neutral_chroma_indication_flag     0
field_seq_flag                     0
frame_field_info_present_flag      0
default_display_window_flag        0
vui_timing_info_present_flag       1
vui_num_units_in_tick              1001
vui_time_scale                     60000
vui_poc_proportional_to_timing_flag 0
hrd_parameters_present_flag        0
bitstream_restriction_flag         0
PPS
pps_pic_parameter_set_id               0
pps_seq_parameter_set_id               0
dependent_slice_segments_enabled_flag  0
output_flag_present_flag               0
num_extra_slice_header_bits            0
sign_data_hiding_flag                  1
cabac_init_present_flag                0
num_ref_idx_l0_default_active          1
num_ref_idx_l1_default_active          1
init_qp                                26
constrained_intra_pred_flag            0
transform_skip_enabled_flag            0
cu_qp_delta_enabled_flag               1
diff_cu_qp_delta_depth             1
pps_cb_qp_offset                         0
pps_cr_qp_offset                         0
pps_slice_chroma_qp_offsets_present_flag 0
weighted_pred_flag                       1
weighted_bipred_flag                     0
transquant_bypass_enable_flag            0
tiles_enabled_flag                       0
entropy_coding_sync_enabled_flag         1
loop_filter_across_slices_enabled_flag   1
deblocking_filter_control_present_flag   0
pps_scaling_list_data_present_flag       0
lists_modification_present_flag          0
log2_parallel_merge_level                2
slice_segment_header_extension_present_flag 0
pps_extension_flag                        0
number of SPS    1
number of PPS      1
number of frames   30
———————————————————————————————————————————————————————————————————

Modify Aspect Ratio Parameters in HEVC/H.265 Elementary Stream

It’s not uncommon when aspect ratio is absent in a hevc elementary stream and a decoder applies a default aspect ratio which does not match. The following python script ModifyHevcAspectRatio.py(python 2.x)

gets sample aspect ratio width and height and update aspect ratio parameters in SPS-VUI.

Usage:

 -i                   input hevc stream
-o                  output hevc stream with modified aspect ratio signaled
 –aspectw     sample aspect ratio width, default 1
–aspecth     sample aspect ratio height, default 1
-n                 number gops to process, if 0 then the whole stream

Example [setting 16:9]:

python ModifyHevcAspectRatio.py -i test.h265 --aspectw 16 --aspecth 9 -o test_aspect.h265

 

 

Note:  ffmpeg also supports modification of aspect ratio without re-encoding, only changing SPS (by using ‘hevc_metadata‘:

Example: for hevc, set the new aspect ratio 1:1 (“square”):
ffmpeg -i swbf.h265  -c:v copy -bsf:v "hevc_metadata=sample_aspect_ratio=1/1" -y swbf_ar.h265
Example: for h264, set the new aspect ratio 4:3
ffmpeg -i swbf.h264  -c:v copy -bsf:v "h264_metadata=sample_aspect_ratio=4/3" -y swbf_ar.h264

Detect Discontinuities in frame_num cadence in AVC/H.264 elementary stream

The syntax element ‘frame_num’ (signaled in the slice header) is not a sort of ‘frame counter’ since frame_num is incremented only if the previous frame is used for reference.

Moreover frame_num is incremented modulo 1<<log2_frame_num ( the syntax element ‘log2_frame_num’ is signaled in SPS).

In case of a network congestion the method usually called as ‘stream thinning’ is applied – to discard frames which not used for reference in order to reduce bitrate (in such case some jitter or short freezes can be observed). Sometimes the whole frame can disappear due to packet losses or it can arrive to a decoder too late to be displayed (relevant for low-latency applications). Loss of used for reference frame causes serious visual distortions since Encoder and Decoder uses different references (they are out-sync), these distortions are cleaned by IDR-frame or by “intra-refresh brush”.

the python script DetectFrameNumGaps.py  (version 2.x) is tailored  to detect gaps in frame_num cadence, python version 3.x – DetectFrameNumGaps3x.py

Usage (identical for bothDetectFrameNumGaps.pyandDetectFrameNumGaps3x.py):

-i        input h264-file
-n       number frames to process, if 0 then the whole stream processed, default 0.
-v       verbose mode  (default false)
Example [check first 10 frames in verbose mode]:
python DetectFrameNumGaps.py -i test.h264 -n 10 -v
naltype   9, offset  0x4
naltype   6, offset  0x9
naltype   6, offset  0x1f
naltype   6, offset  0x30
naltype   7, offset  0x39
SPS
profile_idc            100
constraint_flags 0  0  0  0
level                 40
sps_id                0
chroma_format_idc 1
depth_luma 8,  depth_chroma 8
qpprime_y_zero_transform_bypass_flag 0
Scaling Matrices:
IntraY4x4   not present
IntraCb4x4   not present
IntraCr4x4   not present
InterY4x4   not present
InterCb4x4   not present
InterCr4x4   not present
IntraY8x8   not present
InterY8x8   not present
log2_frame_num        4
naltype   8, offset  0x70
naltype   5, offset  0x77
idr (frame number 0)
slice type      I
frame 0,  frameNum  0
naltype   9, offset  0x3e8
naltype   6, offset  0x3ed
naltype   1, offset  0x3fe
slice type      P
frame 1,  frameNum  1
naltype   9, offset  0x6bb
naltype   6, offset  0x6c0
naltype   1, offset  0x6d1
slice type      B
frame 2,  frameNum  2
naltype   9, offset  0x951
naltype   6, offset  0x956
naltype   1, offset  0x967
slice type      P
frame 3,  frameNum  2
naltype   9, offset  0x1da3
naltype   6, offset  0x1da8
naltype   1, offset  0x1db9
slice type      B
frame 4,  frameNum  3
naltype   9, offset  0x24ab
naltype   6, offset  0x24b0
naltype   1, offset  0x24c1
slice type      P
frame 5,  frameNum  3
naltype   9, offset  0x40fd
naltype   6, offset  0x4102
naltype   1, offset  0x4113
slice type      B
frame 6,  frameNum  4
naltype   9, offset  0x450f
naltype   6, offset  0x4514
naltype   1, offset  0x4525
slice type      P
frame 7,  frameNum  4
naltype   9, offset  0x6922
naltype   6, offset  0x6927
naltype   1, offset  0x6938
slice type      B
frame 8,  frameNum  5
naltype   9, offset  0x6b02
naltype   6, offset  0x6b07
naltype   1, offset  0x6b18
slice type      P
frame 9,  frameNum  5
Total frame count              10
Number of IDRs                 1
Number of gaps in frame_num    0
Example [ frame_num gap detected]:
python DetectFrameNumGaps.py -i test_frame_drop.h264
Frame gap at frame 2 (address 0xf7c2), expected 2, read 3
Total frame count              2565
Number of IDRs                 86
Number of gaps in frame_num   
1
————————————————————————————————————————————————-

Removal of Non-Reference Frames from H.264/AVC Elementary Stream in order to Reduce Bitrate

One of adaptive bitrate methods is to remove non-reference frames (usually B-frames) from a stream. Consequently, the stream bitrate is reduced. In case of network jams removal of non-reference frames is an alternative approach tinstead of switching to lower bitrate stream. Moreover, such stream “thinning” can be easily executed by Middle Boxes without overloading of server.

Removal of non-reference frame might make only jitter visible. Because the frame is not used for reference no drift in reference data between encoder and decoder happens.

I share the python script ThinningH264Stream.py  which performs “thinning” of h264/avc elementary stream.

Usage:

   -i       input h264-file
-o       output h264-file without non-reference frames
-n       number frames to process, if 0 then the whole stream   processed
-v       verbose mode – nal types and other info is printed (default false)
Example:
 python ThinningH264Stream.py  -i test_ibBb_slices_aud.h264 -o qwert.h264 -n 10 -v
input file size 254517
Frame          0
naltype    9 (  aud), ref-idc  0, offset (hex)           0
naltype    7 (  sps), ref-idc  3, offset (hex)           6
naltype    8 (  pps), ref-idc  3, offset (hex)          20
naltype    6 (  sei), ref-idc  0, offset (hex)          29
naltype    5 (  idr), sliceType i, ref-idc  3, offset (hex)         2a4
naltype    5 (  idr), ref-idc  3, offset (hex)         f82   non-first slice
naltype    5 (  idr), ref-idc  3, offset (hex)        1d02   non-first slice
frame size 11420
Frame          1
naltype    9 (  aud), ref-idc  0, offset (hex)        2c9c
naltype    1 (  ipb), sliceType p, ref-idc  2, offset (hex)        2ca2
naltype    1 (  ipb), ref-idc  2, offset (hex)        2ce1   non-first slice
naltype    1 (  ipb), ref-idc  2, offset (hex)        2d96   non-first slice
frame size 286
Frame          2
naltype    9 (  aud), ref-idc  0, offset (hex)        2dba
naltype    1 (  ipb), sliceType b, ref-idc  1, offset (hex)        2dc0
naltype    1 (  ipb), ref-idc  1, offset (hex)        2dd5   non-first slice
naltype    1 (  ipb), ref-idc  1, offset (hex)        2e04   non-first slice
frame size 102
Frame          3
naltype    9 (  aud), ref-idc  0, offset (hex)        2e20
naltype    1 (  ipb), sliceType b, ref-idc  0, offset (hex)        2e26
naltype    1 (  ipb), ref-idc  0, offset (hex)        2e3a   non-first slice
naltype    1 (  ipb), ref-idc  0, offset (hex)        2e4d   non-first slice
frame size 73
Frame          4
naltype    9 (  aud), ref-idc  0, offset (hex)        2e69
naltype    1 (  ipb), sliceType b, ref-idc  0, offset (hex)        2e6f
naltype    1 (  ipb), ref-idc  0, offset (hex)        2e7e   non-first slice
naltype    1 (  ipb), ref-idc  0, offset (hex)        2eaa   non-first slice
frame size 92
Frame          5
naltype    9 (  aud), ref-idc  0, offset (hex)        2ec5
naltype    1 (  ipb), sliceType p, ref-idc  2, offset (hex)        2ecb
naltype    1 (  ipb), ref-idc  2, offset (hex)        2ef8   non-first slice
naltype    1 (  ipb), ref-idc  2, offset (hex)        2fc0   non-first slice
frame size 318
Frame          6
naltype    9 (  aud), ref-idc  0, offset (hex)        3003
naltype    1 (  ipb), sliceType b, ref-idc  1, offset (hex)        3009
naltype    1 (  ipb), ref-idc  1, offset (hex)        3021   non-first slice
naltype    1 (  ipb), ref-idc  1, offset (hex)        30cb   non-first slice
frame size 245
Frame          7
naltype    9 (  aud), ref-idc  0, offset (hex)        30f8
naltype    1 (  ipb), sliceType b, ref-idc  0, offset (hex)        30fe
naltype    1 (  ipb), ref-idc  0, offset (hex)        3110   non-first slice
naltype    1 (  ipb), ref-idc  0, offset (hex)        3139   non-first slice
frame size 97
Frame          8
naltype    9 (  aud), ref-idc  0, offset (hex)        3159
naltype    1 (  ipb), sliceType b, ref-idc  0, offset (hex)        315f
naltype    1 (  ipb), ref-idc  0, offset (hex)        3174   non-first slice
naltype    1 (  ipb), ref-idc  0, offset (hex)        31e2   non-first slice
frame size 173
Frame          9
naltype    9 (  aud), ref-idc  0, offset (hex)        3206
naltype    1 (  ipb), sliceType p, ref-idc  2, offset (hex)        320c
naltype    1 (  ipb), ref-idc  2, offset (hex)        3259   non-first slice
naltype    1 (  ipb), ref-idc  2, offset (hex)        34b4   non-first slice
frame size 801
Number Frames  10
————————————————————————————————————————————————-

Enhancement of  Random Access Ability of H.264/AVC Stream by Insertion of SPS/PPS at IDR frames

Sometimes IDR frames in AVC/H.264 files are not accompanied with SPS/PPS and this disables these frames to be random access points. Actually only the very first frame contains SPS/PPS (otherwise the stream is not decodable).
inserts SPS/PPS (taken from the very first IDR frame) at the start of each IDR frame (provided that frame is absent SPS/PPS) of input elementary stream.
Usage:
python EnhanceH264KeyFrames.py   <input h264 elementary stream>     <output h264 elementary stream>
Example:
python EnhanceH264KeyFrames.py test.h264 enhanced.h264

SPS:

00 00 00 01 67 42 00 1e 8d 68 1e 08 e9 a8 08 08 08

10

PPS:

00 00 00 01 68 ce 09 c8
3 HL headers added

————————————————————————————————————————————————-

Remove Frames Until First Key Frame in H.264/AVC File

Sometimes h264 files start with non-key frame (e.g. if segmentation done at frame boundary and not at key frame boundary, in such case a segment might start from non-key frame). Most of players can’t play such files (e.g. Pot player)

The script CutLeadFramesH264.py seeks the fist key frame. 

All frames prior to the key frame are discarded, the rest of frames are stored in the output file.

Usage:

  -i               input h264 file
-o              output h264 file (started with key frame)
  –idr          if set then key frame is not required to be accompanied with  sps-pps
-v              print details of discarded frames
-d              if the key frame is the very first frame then output file is identical with the input one. If this switch is set then output file is written only if key frame is not at the very
beginning.

Example:

python CutLeadFramesH264.py -i test_no_sps.h264 -o test1.h264 -v –idr

Frame        103, naltype 1,  frameStart  0xd1bb9
Frame        104, naltype 1,  frameStart  0xd2e55
Frame        105, naltype 1,  frameStart  0xd465b
Frame        106, naltype 1,  frameStart  0xd5a9d
Frame        107, naltype 1,  frameStart  0xd77d1
Frame        108, naltype 1,  frameStart  0xd9031
Frame        109, naltype 1,  frameStart  0xdae98
Frame        110, naltype 1,  frameStart  0xdcb33
Frame        111, naltype 1,  frameStart  0xdede7
Frame        112, naltype 1,  frameStart  0xe0dce
Frame        113, naltype 1,  frameStart  0xe2b96
Frame        114, naltype 1,  frameStart  0xeaf0b
Frame        115, naltype 1,  frameStart  0xed80f
Frame        116, naltype 1,  frameStart  0xee851
Frame        117, naltype 1,  frameStart  0xefab2
Frame        118, naltype 1,  frameStart  0xf0cc4
Frame        119, naltype 1,  frameStart  0xf1f4b
Frame        120, naltype 1,  frameStart  0xf36f3
Frame        121, naltype 1,  frameStart  0xf4f7e
Frame        122, naltype 1,  frameStart  0xf6869
Frame        123, naltype 1,  frameStart  0xf8b5a
Frame        124, naltype 1,  frameStart  0xfa090
Frame        125, naltype 1,  frameStart  0xfbf8c
Frame        126, naltype 1,  frameStart  0xfdb06
Frame        127, naltype 1,  frameStart  0xff56b
write output file


 

Get DTS Statistics of both Video and Audio in MPEG2 System (Transport) Stream

In Cloud Gaming timestamps (DTS) of video and audio frames are determined at rendering stage by reading current clock and converting it to 90kHz. Intervals between video/audio timestamps are not necessarily fixed (the rendering rate can drop).  In applications where video is captured from cameras interval between frames can fluctuate too.

The script GetDtsAnalysis.py collects DTS values of both video (H.264/AVC or H.265/HEVC) and audio frames and computes the following statistics:

  • The number of total ts-packets, video ts-packets, audio ts-packets, video frames and audio frames.
  • For both video and audio: 

Minimal interval between two successive DTS values

Maximal interval between two successive DTS values

Average interval between two successive DTS values

Standard deviation (std) of DTS intervals

  • In addition alignment (the maximum and the average) between video and audio DTS is computed: for each video frame DTS the script looks for the audio DTS which is most close to it (this is an useful feature to detect potential li-syncs).
Usage of  GetDtsAnalysis.py:

 -i             input ts-file
-v           whether to print all DTS values

Example:

python GetDtsAnalysis.py -i test.ts
pmtid 33
video_pid  100, audio_pid 101
Collect PTS …
Total number of packets         154728
Number of video packets         116460
Number of audio packets         1896
Number of video frames          355
Number of audio frames          474
Min Video DTS differences in ms       22.5
Max Video DTS differences in ms       46.5
Average Video DTS differences in ms   33.4
Std of Video DTS differences in ms    1.6
Min Audio DTS differences in ms       25.0
Max Audio DTS differences in ms       28.1
Average Audio DTS differences in ms   26.1
Std of Audio DTS differences in ms    0.4
Max DTS Diff between video and audio in ms      12.8
Average DTS Diff between video and audio in ms  6.5
————————————————————————————————————————————————-

Get H265/HEVC Video Statistics from Transport Stream

The script GetHEVCVideoStatsFromTS.py

is tailored to collect statistics – total number oframes, total number of key frames (IDR frame accompanied with VPS, SPS and PPS). The script prints also per frame statistics: NALU type and current frame offset. It’s worth mentioning that access unit delimiter (AUD) is expected at the start of each frame (requested by Mpeg2 System).

Usage:

-i         input transport file (ts) containing hevc elementary stream
-n        number frames to process, if 0 then the whole stream (default 0)

Examples:

[Process 5 frames only  ]

python GetHEVCVideoStatsFromTS.py -i test.ts -n 5

video pid 1024

Key Frame  0, nalType  20, offset  0x24e, pkt  14
Frame  1, nalType  1, offset  0x85ba, pkt  182
Frame  2, nalType  1, offset  0x969e, pkt  205
Frame  3, nalType  1, offset  0xbc12, pkt  256
Frame  4, nalType  1, offset  0xd44e, pkt  289

Frame Count 5, key frames 1
VPS Cnt  1
SPS Cnt  1
PPS Cnt  1

[Process the whole steam  ]

 python GetHEVCVideoStatsFromTS.py -i test.ts

   ….

 Frame  374, nalType  1, offset  0x2c8da6, pkt  15531
Frame  375, nalType  1, offset  0x2c9676, pkt  15543
Key Frame  375, nalType  20, offset  0x2ca00a, pkt  15567
Frame  376, nalType  1, offset  0x2d0616, pkt  15695
Frame  377, nalType  1, offset  0x2d0b3a, pkt  15702
Frame  378, nalType  1, offset  0x2d1292, pkt  15712
Frame  379, nalType  1, offset  0x2d1c1e, pkt  15725
Frame  380, nalType  1, offset  0x2d2376, pkt  15735
Frame  380, nalType  1, offset  0x2d2ad6, pkt  15745
Frame  381, nalType  1, offset  0x2d3226, pkt  15755
Frame  382, nalType  1, offset  0x2d38c2, pkt  15764
Frame  383, nalType  1, offset  0x2d40d6, pkt  15775
Frame  384, nalType  1, offset  0x2d4772, pkt  15784
Frame  385, nalType  1, offset  0x2d4e0e, pkt  15793
Frame  385, nalType  1, offset  0x2d556e, pkt  15803
Frame  386, nalType  1, offset  0x2d5d7a, pkt  15814
Frame  387, nalType  1, offset  0x2d658e, pkt  15825
Frame  388, nalType  1, offset  0x2d6da2, pkt  15836
Frame  389, nalType  1, offset  0x2d7672, pkt  15848
Frame  390, nalType  1, offset  0x2d7ffe, pkt  15861
Frame  390, nalType  1, offset  0x2d88d6, pkt  15873
Frame  391, nalType  1, offset  0x2d9026, pkt  15883
Frame  392, nalType  1, offset  0x2d9a6e, pkt  15897
Frame  393, nalType  1, offset  0x2da282, pkt  15908
Frame  394, nalType  1, offset  0x2daa96, pkt  15919
Frame  395, nalType  1, offset  0x2db1ee, pkt  15929
Frame  395, nalType  1, offset  0x2dbac6, pkt  15941
Frame  396, nalType  1, offset  0x2dc2d2, pkt  15952
Frame  397, nalType  1, offset  0x2dcae6, pkt  15963
Frame  398, nalType  1, offset  0x2dd23e, pkt  15973
Frame  399, nalType  1, offset  0x2ddc86, pkt  15987
Frame  400, nalType  1, offset  0x2de3de, pkt  15997
Key Frame  400, nalType  20, offset  0x2decb6, pkt  16020
Frame  401, nalType  1, offset  0x2e52c2, pkt  16148
Frame  402, nalType  1, offset  0x2e5ad6, pkt  16159
Frame  403, nalType  1, offset  0x2e6172, pkt  16168
Frame  404, nalType  1, offset  0x2e6bba, pkt  16182
Frame  405, nalType  1, offset  0x2e7546, pkt  16195
Frame  405, nalType  1, offset  0x2e7e1e, pkt  16207
Frame  406, nalType  1, offset  0x2e8a92, pkt  16224
Frame  407, nalType  1, offset  0x2e94da, pkt  16238
Frame  408, nalType  1, offset  0x2e9e66, pkt  16251
Frame  409, nalType  1, offset  0x2eaae2, pkt  16268
Frame  410, nalType  1, offset  0x2eb46e, pkt  16281
Frame  410, nalType  1, offset  0x2ebf7a, pkt  16296
Frame  411, nalType  1, offset  0x2ece22, pkt  16316
Frame  412, nalType  1, offset  0x2edcd2, pkt  16336
Frame  413, nalType  1, offset  0x2eedb6, pkt  16359
Frame  414, nalType  1, offset  0x2efe9a, pkt  16382
Frame  415, nalType  1, offset  0x2f11b2, pkt  16408
Frame  415, nalType  1, offset  0x2f264a, pkt  16436
Frame  416, nalType  1, offset  0x2f395a, pkt  16462

Frame Count 416, key frames 17
VPS Cnt  17
SPS Cnt  17
PPS Cnt  17


 

Remove AUDs (Access Unit Delimiters) from H.264/AVC and H.265/HEVC Elementary Streams

The scripts RemoveAudsInH264Stream.py and RemoveAudsInHEVCStream.py

Remove AUDs (access unit delimiters) from AVC/H.264 and HEVC/H.265  elementary streams.  Number of removed AUDs is reported at the end.

If no AUD is present then output file is not generated and corressponding print issued.

Usage:

RemoveAudsInHEVCStream.py  in.h265  out.h265 
RemoveAudsInH264tream.py  in.h264  out.h264 

Example:

python RemoveAudsInHEVCStream.py    test.h265    test_no_auds.h265


Analyzing …
||===========================================================>                    |||
AUDs to remove 59


Print pic_parameter_set_id of each frame in H.264/AVC stream, relevant if multiple PPS are present

Motivation:  Some NVIDIA encoders (e.g. T4) supports Weighted Prediction mode (specified by the parameter enableWeightedPrediction in NV_ENC_INITIALIZE_PARAMS structure). If enableWeightedPrediction  is on then the encoder generates two PPS – the first PPS (with pic_parameter_set_id=0) enables Weighted Prediction (weighted_pred_flag=1), the second PPS (with pic_parameter_set_id=1) disables Weighted Prediction.

In the slice header of each frame the first PPS is chosen if the weighted prediction is activated in this frame, otherwise the second PPS is assigned.  It’s desirable to know how many frames in the stream utilize the weighted prediction. In other words how many frames carry pic_parameter_set_id=0 in theirs slice headers.

The script  GetFramePpsIdH264.py  traverses H.264/AVC elementary stream and prints: the following info per frame:

  • Frame number
  • Frame offset in bytes
  • Slice Nal type (5 – for IDR and 1 for others)
  • pic_parameter_set_id

Usage:

-i     input h264-file
-n    number frames to process, if 0 then the whole stream  processed

Example:

python GetFramePpsIdH264.py -i test_wp.h264

           …..

Frame    679,  Offset     1d4847,  NalType   1,  ppsid   1
Frame    680,  Offset     1d4dbb,  NalType   1,  ppsid   1
Frame    681,  Offset     1d53c1,  NalType   1,  ppsid   1
Frame    682,  Offset     1d5836,  NalType   1,  ppsid   1
Frame    683,  Offset     1d5d7e,  NalType   1,  ppsid   1
Frame    684,  Offset     1d63ba,  NalType   1,  ppsid   1
Frame    685,  Offset     1d6931,  NalType   1,  ppsid   1
Frame    686,  Offset     1d6de7,  NalType   1,  ppsid   1
number frames with pps_id=0     0
number frames with pps_id=1     687

 


 

Print weighted prediction parameters per frame/slice (for H.264/AVC) streams,  adapted for Python 3.x

Sometimes it’s worth to know whether your encoder chooses non-trivial weighted prediction (and on what frames). 

The following python 3.x script PrintWPStatisticsH264.py parses all P-slice headers and print weighted prediction parameters (if present). Multiple PPS supported.

Multiple slices supported. Notice that only P-slices are analyzed for weighted prediction.

Usage:

-i     input h264-file
-n    number frames to process, if 0 then the whole stream  processed

Example [weighted prediction is off, four slices per frame]

python PrintWPStatisticsH264.py -i  test.h264
Frame    0
    SliceCnt 0, I-slice,  first_mb_in_slice    0
    SliceCnt 1, I-slice,  first_mb_in_slice    2040
    SliceCnt 2, I-slice,  first_mb_in_slice    4080
    SliceCnt 3, I-slice,  first_mb_in_slice    6120
Frame    1
    SliceCnt 0, P-slice,  first_mb_in_slice    0
weighted prediction off
    SliceCnt 1, P-slice,  first_mb_in_slice    2040
weighted prediction off
    SliceCnt 2, P-slice,  first_mb_in_slice    4080
weighted prediction off
    SliceCnt 3, P-slice,  first_mb_in_slice    6120
weighted prediction off
Frame    2
    SliceCnt 0, P-slice,  first_mb_in_slice    0
weighted prediction off
    SliceCnt 1, P-slice,  first_mb_in_slice    2040
weighted prediction off
    SliceCnt 2, P-slice,  first_mb_in_slice    4080
weighted prediction off
    SliceCnt 3, P-slice,  first_mb_in_slice    6120
weighted prediction off

….

Example [weighted prediction is activates on some frames, switching the weighted prediction on frame level is allowed by multiple PPS, the first PPS with weighted_pred_flag=off and another on ]

python PrintWPStatisticsH264.py -i  test1.h264

Frame    41
    SliceCnt 0, P-slice,  first_mb_in_slice    0
weighted prediction off
    SliceCnt 1, P-slice,  first_mb_in_slice    2040
weighted prediction off
    SliceCnt 2, P-slice,  first_mb_in_slice    4080
weighted prediction off
    SliceCnt 3, P-slice,  first_mb_in_slice    6120
weighted prediction off
Frame    42
    SliceCnt 0, P-slice,  first_mb_in_slice    0
luma_log2_weight_denom      5
chroma_log2_weight_denom    5
Reference Picture  0
luma_weight_l0    33
luma_offset_l0    0
chroma_weight_cb   33
chroma_offset_cb   -4
chroma_weight_cr   33
chroma_offset_cr   -3
    SliceCnt 1, P-slice,  first_mb_in_slice    2040
luma_log2_weight_denom      5
chroma_log2_weight_denom    5
Reference Picture  0
luma_weight_l0    33
luma_offset_l0    0
chroma_weight_cb   33
chroma_offset_cb   -4
chroma_weight_cr   33
chroma_offset_cr   -3
    SliceCnt 2, P-slice,  first_mb_in_slice    4080
luma_log2_weight_denom      5
chroma_log2_weight_denom    5
Reference Picture  0
luma_weight_l0    33
luma_offset_l0    0
chroma_weight_cb   33
chroma_offset_cb   -4
chroma_weight_cr   33
chroma_offset_cr   -3
    SliceCnt 3, P-slice,  first_mb_in_slice    6120
luma_log2_weight_denom      5
chroma_log2_weight_denom    5
Reference Picture  0
luma_weight_l0    33
luma_offset_l0    0
chroma_weight_cb   33
chroma_offset_cb   -4
chroma_weight_cr   33
chroma_offset_cr   -3
Frame    43
    SliceCnt 0, P-slice,  first_mb_in_slice    0
weighted prediction off
    SliceCnt 1, P-slice,  first_mb_in_slice    2040
weighted prediction off
    SliceCnt 2, P-slice,  first_mb_in_slice    4080
weighted prediction off
    SliceCnt 3, P-slice,  first_mb_in_slice    6120
weighted prediction off

 

Parse SPS and Print Resolution (H.264/AVC)

Sometimes encoders change resolution at the middle of video stream to cope with instabilities in transmission mediums.  By the way Nvidia SDK has even a special routine

NvEncReconfigureEncoder to reconfigure video resolution on the fly without resetting of the encoder.

The script PrintResolutionsInH264ES.py  is tailored to traverse input H264/AVC elementary stream and print resolutions signaled in SPS.

Usage:

-i                  input h264-file

  -n                 number frames to process, if 0 then the whole stream processed

  -v                  print other SPS parameters in addition to resolution  (default false)

  -q                  print resolution signaled in SPS only if it is changed (default false), if it is on then verbose set to false

Example [ print resolutions signaled in all SPS (regardless whether the resolution changed or not), plus printing other SPS parameters

python PrintResolutionsInH264ES.py -i test.h264  -v

SPS
profile_idc           100
level                 40
sps_id                0
pic_order_cnt_type    0
max_pic_order_cnt     32768
max_num_ref_frames    1
Frame  0 (offset 0x0), width  704, height  576
SPS
profile_idc           100
level                 40
sps_id                0
pic_order_cnt_type    0
max_pic_order_cnt     32768
max_num_ref_frames    1
Frame  25 (offset 0x285e0), width  704, height  576
SPS
profile_idc           100
level                 40
sps_id                0
pic_order_cnt_type    0
max_pic_order_cnt     32768
max_num_ref_frames    1
Frame  50 (offset 0x4e4f3), width  704, height  576
SPS
profile_idc           100
level                 40
sps_id                0
pic_order_cnt_type    0
max_pic_order_cnt     32768
max_num_ref_frames    1
Frame  75 (offset 0x76297), width  704, height  576
SPS
profile_idc           100
level                 40
sps_id                0
pic_order_cnt_type    0
max_pic_order_cnt     32768
max_num_ref_frames    1
Frame  100 (offset 0x9de9a), width  704, height  576
….

Example [ print resolutions (signaled in all SPS) only if they are changed]

python PrintResolutionsInH264ES.py -i test.h264 -q

Frame  0 (offset 0x0), width  1920, height  1088
Frame  300 (offset 0x59608c), width  1888, height  1088
Frame  600 (offset 0xe6da04), width  1872, height  1088
Frame  900 (offset 0x170bb55), width  1856, height  1088
Frame  1200 (offset 0x212048b), width  1840, height  1088
Frame  1500 (offset 0x2b0a028), width  1824, height  1088
Frame  1800 (offset 0x351791c), width  1808, height  1088
Frame  2100 (offset 0x3efced9), width  1792, height  1088
Frame  2400 (offset 0x48f557f), width  1776, height  1088
Frame  2700 (offset 0x534dde0), width  1760, height  1088
Frame  3000 (offset 0x5db3029), width  1744, height  1088
Frame  3300 (offset 0x681b400), width  1728, height  1088
Frame  3600 (offset 0x722507e), width  1712, height  1088
Frame  3900 (offset 0x7c509eb), width  1696, height  1088
Frame  4200 (offset 0x86d3afd), width  1680, height  1088
Frame  4500 (offset 0x91687f0), width  1664, height  1088
Frame  4800 (offset 0x9be119c), width  1648, height  1088

 


Get Number Slices in H.264/HEVC Streams

The script  GetNumberSlicesPerFrameInH264 (adapted for Python 3.x) is tailored to count and print the number of slices per frame for input h264/avc file. This script is relevant in analysis of dynamic slicing mode. In addition the script GetNumberSlicesPerFrameInHEVC   count number of slices per frame for hevc elementary stream

Usage:

-i       input h264-file (elementary stream)
-n      number of frames to process, if 0 (default) then the whole stream processed

 

Example [ h264 case, print slices per frame for first 10 frames]

python GetNumberSlicesPerFrameInH264.py -i crowdrun1080p_slices.h264 -n 10
Frame      0
slices     4
Frame      1
slices     4
Frame      2
slices     4
Frame      3
slices     4
Frame      4
slices     4
Frame      5
slices     4
Frame      6
slices     4
Frame      7
slices     4
Frame      8
slices     4
Frame      9
slices     4
Example [ hevc case, print slices per frame for first 10 frames]

python GetNumberSlicesPerFrameInHEVC.py -i test_slices5.h265 -n 10


frame start 0, position 0x0
slices 5

frame start 1, position 0x25d5d
slices 5

frame start 2, position 0x465de
slices 5

frame start 3, position 0x52cce
slices 5

frame start 4, position 0x5bfc5
slices 5

frame start 5, position 0x658de
slices 5

frame start 6, position 0x6f844
slices 5

frame start 7, position 0xa0df5
slices 5

frame start 8, position 0xb1786
slices 5

frame start 9, position 0xbd830

————————————————————————————————————————————————-

Parse SPS and Print Custom Scaling Matrices in H.264/AVC

The script PrintScalingMatricesH264.py parses SPS of H.264/AVC elementary stream and prints custom scaling matrices (if present),  the script is adapted for Python 3.x

Usage:

-i       input h264-file (elementary stream)
-n      number of frames to process, if 0 (default) then the whole stream processed

 

Example [ process 20 first frames and parse all SPS]

python PrintScalingMatricesH264.py -i test.h264 -n 20
Frame  0 (offset 0x0)
SPS:
profile_idc           100
level                 40
sps_id                0
scaling_list_present_flag[0]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[1]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[2]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[3]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[4]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[5]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[6]    1
scales (8×8):
16 18 20 22 24 26 28 30 18 18 22 24 26 28 30 32 20 22 24 26 28 30 32 34 22 24 26 28 30 32 34 36 24 26 28 30 32 34 36 39 26 28 30 32 34 36 39 42 28 30 32 34 36 39 42 45 30 32 34 36 39 42 45 48
    scaling_list_present_flag[7]    1
scales (8×8):
16 18 20 22 24 26 28 30 18 18 22 24 26 28 30 32 20 22 24 26 28 30 32 34 22 24 26 28 30 32 34 36 24 26 28 30 32 34 36 39 26 28 30 32 34 36 39 42 28 30 32 34 36 39 42 45 30 32 34 36 39 42 45 48
Frame  16 (offset 0x10215)
SPS:
profile_idc           100
level                 40
sps_id                0
scaling_list_present_flag[0]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[1]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[2]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[3]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[4]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[5]    1
scales (4×4):
16 21 26 31 21 26 31 36 26 31 36 41 31 36 41 48
    scaling_list_present_flag[6]    1
scales (8×8):
16 18 20 22 24 26 28 30 18 18 22 24 26 28 30 32 20 22 24 26 28 30 32 34 22 24 26 28 30 32 34 36 24 26 28 30 32 34 36 39 26 28 30 32 34 36 39 42 28 30 32 34 36 39 42 45 30 32 34 36 39 42 45 48
    scaling_list_present_flag[7]    1
scales (8×8):
16 18 20 22 24 26 28 30 18 18 22 24 26 28 30 32 20 22 24 26 28 30 32 34 22 24 26 28 30 32 34 36 24 26 28 30 32 34 36 39 26 28 30 32 34 36 39 42 28 30 32 34 36 39 42 45 30 32 34 36 39 42 45 48

 

————————————————————————————————————————————————-

Compare SPS in H264/AVC stream

Sometimes h264/avc stream is spliced from several segments and each segment carries its own SPS.

The script  CompareSPSinH264Ver3.py (adapted for Python version 3.x) compare SPS along the whole h264/avc stream and prints differences (if present), actually the very first SPS is compared with all other SPS

Usage:

-i              input h264-file
-v           verbose mode  (default false)

Example:

python CompareSPSinH264Ver3.py -i test.h264
first SPS size  0x77 (119)
SPS at address 61cdd differ in size with first sps (first len 119, current len 118)
SPS at address 104fac differ in size with first sps (first len 119, current len 118)
SPS at address 1a870b differ in size with first sps (first len 119, current len 118)
SPS at address 24b810 differ in size with first sps (first len 119, current len 118)

————————————————————————————————————————————————-

Get Instant Bitrates from H264 File

The script GetBitrate.py   computes instant bitrates over h264 file with a predeifined window and hop, the script prints the average instant bitrate and the peak bitrate.
By deafult the bitrate is measured over non-overlapping windows of duration 1s.
Usage:
-i                      input h264-file
-n                     number frames to process, if 0 then the whole stream processed
-v                    verbose mode (default false)
-p                   print progress (default false), set fault if verbose is ON
-f                   frame rate either 30 or 60, default 60
–overlap     overlaping window with hop of frame duration (default false)
–winsize     window size in seconds for calculation instant bitrate (default 1)

Example:

python GetBitrate.py -i    crowdrun1080p.h264 -f 60

number of frames 1000

non-overlap windowing, window size 1s, average bitrate (Mbps) 21.2264, maxbitrate (Mbps) 23.6517

Note:

ffprobe is declared as supporting computation of the average bitrate, but for elementary streams ffprobe provides nothing

ffprobe -v quiet -show_entries stream=bit_rate -of default=noprint_wrappers=1 test.h264
bit_rate=N/A

Moreover, i found that for mpegts streams results of ffprobe bitrate computation are wrong, for test.ts with video bitrate ~5Mbps, ffprobe shows incorrect result:

ffprobe -v quiet -show_entries stream=bit_rate -of default=noprint_wrappers=1 test.ts
bit_rate=192141
bit_rate=N/A
bit_rate=192141
bit_rate=N/A

 

In addition there are two free bitrate viewers:

 

————————————————————————————————————————————————-

Get Instant Bitrates from HEVC File

The script GetHEVCBitrate.py   computes instant bitrates over hevc file with a predeifined window and hop, the script prints the average instant bitrate and the peak bitrate.
By deafult the bitrate is measured over non-overlapping windows of duration 1s.

Usage:

-i                                              input hevc-file
-n                                            number frames to process, if 0 then the whole stream processed
-v                                            verbose mode (default false)
-p                                            print progress (default false), set fault if verbose is ON
-f                                            frame rate (integer), default 60
–overlap                               overlaping window with hop of frame duration (default false)
–winsize                             window size in seconds for calculation instant bitrate (default 1)

 

Example:

python GetHevcBitrate.py -i crowd_2Mbps_deadzone_50_ver2.hevc -f 50

number of frames 500

non-overlap windowing, window size 1s, average bitrate (Mbps) 2.1179, maxbitrate (Mbps) 3.7076

 

————————————————————————————————————————————————-

Remove Specific SEI Messages from HEVC File

Sometimes SEI messages in HEVC/H.265 streams get redundant (or even can mislead decoders). Therefore i provide the script RemoveSeiFromHevcStream.py to remove all SEI messages from the input HEVC/H.265 elementary stream (“SEI thinning”)

The script works at GOP level (to be easy in future to parallel it), the script reads the whole GOP, analyzes it, removes SEIs and writes SEI-free GOP to output file.

Usage:

-i             input hevc fifo or file
-o           output hevc tiled stream
-v           whether to print naltypes and offsets (default false)

Example:

 python RemoveSeiFromHevcStream.py -i test.h265 -o qwert.h265
gop 0, address  0x0
gop 1, address  0xd2535
gop 2, address  0x146657
removed seis  31

 

———————————————————————————————————————————————————————————————————

Get Meta Data of HEVC Stream in MP4-file

The script GetMetaHevcHdr searches video track in mp4-file and checks whether the track contains hevc-stream (‘hev1’ or ‘hvc1’ are present). If hevc-stream is presnt then hvcC-box is parsed and the stream’s information printed:

Usage:

-i      input mp4-file containing hevc stream
-d     dump headers vps, sps and pps as vps.bin, sps.bin and pps.bin (default false),  note – strat code ’00 00 00 01′ added.

 

Example:

python GetMetaHevcHdr.py -i strange.mp4

 

C:\Tools>python GetMetaHevcHdr.py -i strange.mp4

video trak number 0
resolution 1920×1080
Configuration Version 0x1
profile Main
compatibility_flags 60000000
progressive_source_flag 0
interlaced_source_flag 0
non_packed_constraint_flag 0
frame_only_constraint_flag 0
level_idc 0x78 (120)
min_spatial_segmentation_idc 0x0
parallelismType 0
chromaFormat 1
luma_bit_depth 8
chroma_bit_depth 8
avgFrameRate 0
constantFrameRate 0
numTemporalLayers 3
temporalIdNested 0
nal_unit_size 4
high level headers 3

Hexstr 0x20
array_completeness 0
header type VPS
nal_count 1
hdr_len 30

Hexstr 0x21
array_completeness 0
header type SPS
nal_count 1
hdr_len 66

Hexstr 0x22
array_completeness 0
header type PPS
nal_count 1
hdr_len 9


Detect Large Frames in H264/AVC Elementary Stream

We determine large frames as frames with sizes exceeding much the expected size for a given bitrate. Such spike frames can impact on latency and cause network congestions. I-frames or P-frames at a scene cut or a flaw in Rate Control can generate large frames. 

It’s obvious that the stream should be generated by an encoder configured with CBR mode or in VBR mode. If the encoder is configured with CRF or constant QP mode then frame size fluctuations are caused by the complexity of scenes.

The script DetectLargeFramesInH264     is tailored to detect large frames in h264/avc elementary stream. This script works in dual-pass mode, in the first pass it gathers statisitics and in the second pass the script processes collected data. In addition to number of large frames the scripts compute average and peak bitrates (taken over the window of 1s).

This script is dedicated to check Rate Control in CBR/VBR modes.

Usage:

-i        input h264-file
-n       number frames to process, if 0 then the whole stream processed
-s       skip first frames due to ubstability Rate Control at the start, default 0
-t        target bitrate in Mbps, default 10Mbps
-v        verbose mode, print numbers of large frames (default false)
-f         frame rate, default 60
-a        large frame scale factor, when the size of a frame in bytes exceeds ‘factor’ x ‘expected_size’ it is called Large Frame, default 1.5x above the expected

 

Example [ find large frames exceeding the expectd size by 1.5x or more, the traget bitrate is 10Mbps, the frame rate 60fps, skip first 10 frames due to instability of Rate Control at the start]

python DetectLargeFramesInH264.py -i test_vbr_10Mbps.h264 -s 10 -t 10 -v -f 60 -a 1.5

Expected frame size 20833 bytes
number of frames 765
62 frame type pb frame size 31432 (expected 20833)
387 frame type pb frame size 33387 (expected 20833)
480 frame type idr frame size 31412 (expected 20833)
540 frame type idr frame size 32222 (expected 20833)
660 frame type idr frame size 32998 (expected 20833)
720 frame type idr frame size 34312 (expected 20833)

average bitrate (Mbps) 9.5568, peak bitrate (Mbps) 9.9682

number Large Frames (scale threshold 1.500000) 6

 

Note:  frame type is either ‘idr’ or ‘pb’ including P,B or non-IDR I-frame


Detect Large Frames in HEVC Elementary Stream

Description is similar to that of h264 case – Detect Large Frames in H264/AVC Elementary Stream

The script DetectLargeFramesInHEVC    is tailored to detect large frames in hevc elementary stream. This script works in dual-pass mode, in the first pass it gathers statisitics and in the second pass the script processes collected data. In addition to number of large frames the scripts compute average and peak bitrates (taken over the window of 1s).

This script is dedicated to check Rate Control in CBR/VBR modes.

Usage:

-i        input hevc-file
-n       number frames to process, if 0 then the whole stream processed
-s       skip first frames due to ubstability Rate Control at the start, default 0
-t        target bitrate in Mbps, default 10Mbps
-v        verbose mode, print numbers of large frames (default false)
-f         frame rate, default 60
-a        large frame scale factor, when the size of a frame in bytes exceeds ‘factor’ x ‘expected_size’ it is called Large Frame, default 1.5x above the expected

 

Example [ find large frames exceeding the expectd size by 1.4x or more, the traget bitrate is 15Mbps, the frame rate 60fps]

python DetectLargeFramesInHEVC.py -i  test_vbr_15M.h265 -s 0 -t 15 -v -f 60 -a 1.4

Expected frame size 31250 bytes
number of frames 557
0 frame type idr frame size 43967 (expected 31250)
148 frame type pb frame size 45329 (expected 31250)
478 frame type pb frame size 44949 (expected 31250)
549 frame type pb frame size 44957 (expected 31250)

average bitrate (Mbps) 13.4108, peak bitrate (Mbps) 14.9510

number Large Frames (scale threshold 1.400000) 4

 

Note:  frame type is either ‘idr’ or ‘pb’ including P,B, I, CRA

 


Find Video Track Number in mp4-file

The script GetVideoTrakNo.py  (adapted for Python 3.x) , looks for the video track (actually for  the first video track) in mp4-file’s metadata and prints the track number (counted from one, not from zero, the reason MP4Box tool uses track numbers counted from 1).

Example:

python GetVideoTrakNo.py test.mp4
Video Track 1

 

Note:

video track can have number 2 or even higher.

Example:

python GetVideoTrakNo.py  box.mp4
Video Track 2

mp4box -info box.mp4

* Movie Info *
Timescale 90000 – Duration 00:00:15.184
Fragmented File no – 2 track(s)
File Brand isom – version 0
Created: GMT Mon Jun 09 15:54:39 2014

File has no MPEG4 IOD/OD

Track # 1 Info – TrackID 1 – TimeScale 44100 – Duration 00:00:15.123
Media Info: Language “Undetermined” – Type “soun:mp4a” – 579 samples
MPEG-4 Config: Audio Stream – ObjectTypeIndication 0x6b
MPEG-1 Audio – 1 Channel(s) – SampleRate 44100 – Layer 2
Self-synchronized

Track # 2 Info – TrackID 2 – TimeScale 1000000 – Duration 00:00:15.217
Media Info: Language “Undetermined” – Type “vide:avc1” – 456 samples
Visual Track layout: x=0 y=0 width=640 height=480
MPEG-4 Config: Visual Stream – ObjectTypeIndication 0x21
AVC/H264 Video – Visual Size 640 x 480
AVC Info: 1 SPS – 1 PPS – Profile High @ Level 3
NAL Unit length bits: 32
Pixel Aspect Ratio 1:1 – Indicated track size 640 x 480
Synchronized on stream 1

 


 

Print Mpegts Packets Payload Sizes

The script PrintPayloadSize.py (adapted for Python 3.x) , traverses mpegts file until PAT/PMT found, then the script parses PAT/PMT to figire out video PID (h264 or hevc).

Then the scripts parses video mpegts packets and prints payload sizes (incl. PES headers).

Usage:

-i       input mpegts-file (must contain video – h264 or hevc)
-n     number of mpegts video packets to parse, if 0 then all, (default 0)

Note: For each packet three magnitudes are printed out:

packet number       video packet number     payload size in bytes

its commonly mpegts contains not only video packets, therefore packet number differs from video packet number.

Example [ process first 200 video packets]:

python PrintPayloadSize.py -i   test_real_clock.ts -n 200

Pkt 1588, vpkt 0, mpegts-payload 182   // first video packet is 1588 packet from the beginning of the file and has payload 182 (due to adaptation field, since it’s a frame start)
Pkt 1589, vpkt 1, mpegts-payload 184
Pkt 1590, vpkt 2, mpegts-payload 184
Pkt 1591, vpkt 3, mpegts-payload 184

Pkt 1773, vpkt 180, mpegts-payload 184
Pkt 1774, vpkt 181, mpegts-payload 184
Pkt 1775, vpkt 182, mpegts-payload 184
Pkt 1776, vpkt 183, mpegts-payload 184
Pkt 1777, vpkt 184, mpegts-payload 184
Pkt 1778, vpkt 185, mpegts-payload 81   // the last packet in a frame, the payload size is not 184
Pkt 1779, vpkt 186, mpegts-payload 182   // the first packet in the next frame
Pkt 1780, vpkt 187, mpegts-payload 184
Pkt 1781, vpkt 188, mpegts-payload 184
Pkt 1782, vpkt 189, mpegts-payload 184
Pkt 1783, vpkt 190, mpegts-payload 184
Pkt 1784, vpkt 191, mpegts-payload 184
Pkt 1785, vpkt 192, mpegts-payload 184
Pkt 1786, vpkt 193, mpegts-payload 184
Pkt 1787, vpkt 194, mpegts-payload 184
Pkt 1788, vpkt 195, mpegts-payload 184
Pkt 1789, vpkt 196, mpegts-payload 184
Pkt 1790, vpkt 197, mpegts-payload 184
Pkt 1791, vpkt 198, mpegts-payload 184
Pkt 1792, vpkt 199, mpegts-payload 184

 

 


 

Cut H264/AVC Elementary Stream at Specified IDR

The python script CutH264AtIDR.py  traverses the input stream until it detects k-th IDR (specified in the command line), then the script cuts the input stream at this IDR and the tail is saved as output h264 stream. 

Usage: 

 -i         input h264-file

 -o        output h264-file, cut at the specified k-th IDR

  -n       IDR number to cut (k-th IDR), must be greater 1, default 2 (i.e. at the second IDR to cut)

 

Examples:

python CutH264.py -i test.h264 -o test_tail.h264 -n 2

2-th idr found at offset 0x2c303a8, frame 3342

If k-th IDR not found then the output file is empty and a corresponding print is issued, e.g

python CutH264.py -i test.h264 -o test_tail.h264 -n 20

20-th idr not found, empty output file

 


 

Count Short Start-Codes

The H264/AVC spec. contains two types of start-codes:  long (32 bits, 00 00 00 01) and short (24 bits, 00 00 01)

During my career, once I was faced with a peculiar  h264 decoder which ignored short start-codes (and respected long start codes). This decoder could not find the slice start if the start code was short.

I share the python script CountShortStartCodesInH264.py which detects and count short start codes in h264 file. In addtion this script counts the number of frames.

 

Usage:

-i          input h264 file
-v         verbose mode, print offset of short start code (default false)
-n         number frames to process, if 0 then the whole stream processed

 

Example:

python CountShortStartCodesInH264.py -i  battlefield_hp_cbr16_10M.h264

number of detected frames 557
number of short start codes 1672

Note:  why number of start codes is greater than the number of frames? In the given stream each frame is divided into four slices. Thus each frame contains four start codes.

 

Example (process two frames in verbose mode)

python CountShortStartCodesInH264.py -i battlefield_hp_cbr16_10M.h264 -n 2 -v

short start code at offset 4467 (0x1173)
short start code at offset 8600 (0x2198)
short start code at offset 15524 (0x3ca4)
short start code at offset 20005 (0x4e25)

number of detected frames 2
number of short start codes 4

 


 

Parse PNG Image Format

 

PNG is still-popular lossless format, despite it was developed at the middle of 90x.  PNG file consists of chunks (control and data), each chunk ends with 4-bytes CRC code. Thus, the png-format is highly error resilient. 

The python script ParsePNSG , adapted for 3.x version, is tailored to parse PNG file.

 

Example:

python ParsePNG.py -i intra-modes.png

 

chunk name: IHDR
chunk size: 13, offset 0x8
width 632
height 677
bit-depth 8
palette used 0
color image 1

chunk name: sRGB
chunk size: 1, offset 0x21

chunk name: gAMA
chunk size: 4, offset 0x2e

chunk name: pHYs
chunk size: 9, offset 0x3e

chunk name: IDAT
chunk size: 65445, offset 0x53

chunk name: IDAT
chunk size: 65524, offset 0x10004

chunk name: IDAT
chunk size: 65524, offset 0x20004

chunk name: IDAT
chunk size: 65524, offset 0x30004

chunk name: IDAT
chunk size: 65524, offset 0x40004

chunk name: IDAT
chunk size: 51084, offset 0x50004

number of chunks 10

————————————————————————————————————————————————————-

Parse Slice Header and Print Weighted Prediction Parameters, H.264

The weighted prediction was adopted by H.264/AVC. In some video sequences, in particular those containg fading between scenes, the current picture  is more strongly correlated to a reference picture scaled by a weighting factor.

The python script PrintWeightsH264  gets as input H.264/AVC stream and parses SPS, PPS and slice headers. The script prints the weghted prediction information if it’s present in the slice header.

Usage:

-i         input h264-file
-n        number frames to process, if 0 then the whole stream processed, default 0
-v        verbose mode, print SPS and PPS info (default false)

Example [encode 4 frames in verbose mode (i.e. printing SPS and PPS)]:

python PrintWeightsH264.py    -i test.h264    -n 3     -v

SPS
profile_idc 100
level_idc    50 
sps_id          0
log2_max_frame_num 4
log2_max_pic_order_cnt 10
max_num_ref_frames 1
gaps_in_frame_num_value_allowed_flag 0
pic_width_in_mbs 120
pic_width_in_mbs 68
frame_mbs_only_flag 1
direct_8x8_inference_flag 1

PPS
pps_id 0
sps_id 0
bottom_field_pic_order_in_frame_present_flag 0
num_ref_idx_l0 1
num_ref_idx_l1 1
weighted_pred_flag 1
weighted_bipred_idc 0
pic_init_qp 26
chroma_qp_index_offset 0
deblocking_filter_control_present_flag 0
constrained_intra_pred_flag 0

Frame 0


Slice
first_mb 0
slice type I
pps_id 0
frame_num 0
idr_idx 0
POC 0

Frame 1


Slice
first_mb 0
slice type P
pps_id 0
frame_num 1
POC 2
override flag 0
ref_pic_list_modification_flag_l0 0
luma_log2_weight_denom 5
chroma_log2_weight_denom 5
luma_weight_l0_flag 1
luma_weight_l0 33
luma_offset_l0 -2
chroma_weight_l0_flag 0

 

Frame 2


Slice
first_mb 0
slice type P
pps_id 0
frame_num 2
POC 4
override flag 0
ref_pic_list_modification_flag_l0 0
luma_log2_weight_denom 5
chroma_log2_weight_denom 5
luma_weight_l0_flag 0
chroma_weight_l0_flag 0

 

Frame 3


Slice
first_mb 0
slice type P
pps_id 0
frame_num 3
POC 6
override flag 0
ref_pic_list_modification_flag_l0 0
luma_log2_weight_denom 5
chroma_log2_weight_denom 5
luma_weight_l0_flag 0
chroma_weight_l0_flag 0

 

————————————————————————————————————————————————————-

Get PSNR, SSIM and VIFP (Visual Information Fidelity) with sewar package

The ‘sewar’ package contains different image and video quality metrics (PSNR,SSIM,MS-SSIM,VIFP etc.).

To install ‘sewar’ type: pip install sewar

The scripts GetPsnr.py , GetSSIM.py and GetVifp.py take as input reference and deformed yuv420p@8bpp video sequences and computes PSNR, SSIM and VIFP (Visual Information Fidelity) score per picture.

Usage:

-f                reference yuv-sequence (yuv420p@8bpp)
-d               deformed yuv-sequence (yuv420p@8bpp) 
-n               number of frames to process, if 0 then all frames, default 0
--width      width in pixels, must be even
--height     height in pixels, must be even

 

Example [ compute psnr values of first 10 frames]

python GetPsnr.py --width 1920 --height 1080 -f Fifa17_1920x1080.yuv -d fifa_hp_cbr16_10M.yuv -n 10


frame 0, psnr 30.53
frame 1, psnr 32.48
frame 2, psnr 33.20
frame 3, psnr 34.46
frame 4, psnr 33.86
frame 5, psnr 33.63
frame 6, psnr 33.35
frame 7, psnr 33.09
frame 8, psnr 32.88
frame 9, psnr 32.60

average psnr 33.01

 

Example [ compute SSIM values of first 10 frames]

python GetSsim.py --width 1920 --height 1080 -f Fifa17_1920x1080.yuv -d fifa_hp_cbr16_10M.yuv -n 10


frame 0, ssim 0.84
frame 1, ssim 0.87
frame 2, ssim 0.89
frame 3, ssim 0.91
frame 4, ssim 0.90
frame 5, ssim 0.90
frame 6, ssim 0.90
frame 7, ssim 0.90
frame 8, ssim 0.89
frame 9, ssim 0.89

average ssim 0.89

 

Example [ compute VIFP scores of first 10 frames]

python GetVifp.py --width 1920 --height 1080 -f Fifa17_1920x1080.yuv -d fifa_hp_cbr16_10M.yuv -n 10


frame 0, vifp 0.29
frame 1, vifp 0.36
frame 2, vifp 0.40
frame 3, vifp 0.44
frame 4, vifp 0.43
frame 5, vifp 0.43
frame 6, vifp 0.43
frame 7, vifp 0.42
frame 8, vifp 0.42
frame 9, vifp 0.41

average vifp 0.40

Note: to get (or to extract) yuv-sequence from encoded stream i use ffmpeg tool:

ffmpeg -i fifa_hp_cbr16_15M.h264 -pixel_format yuv420p -frames 100 -y fifa_hp_cbr16_15M.yuv

 

 

————————————————————————————————————————————————————-

Print NAL Headers of HEVC Elementary Stream

The python script GetHevcNalTypes.py takes input HEVC elementary stream and print NAL unit types and layer IDs (relevant for scalable streams). 

If layerID (nuh_layer_id) is non-zero then NAL data belong to enchancement layer, if layerID=0 then the current NAL unit belongs to base layer.

Usage:

-i  input hevc file
-n  number of frames to process, if 0 then all, (default 0)

 

Example:  dual SNR scalable stream

python GetHevcNalTypes.py -i dual_layer.h265

nal-type 32 VPS, layerId 0
nal-type 33 SPS, layerId 0
nal-type 33 SPS, layerId 1
nal-type 34 PPS, layerId 0
nal-type 19 IDR, layerId 0
nal-type 34 PPS, layerId 1
nal-type 19 IDR, layerId 1
nal-type 1 PB, layerId 0
nal-type 5 STSA, layerId 1
nal-type 1 PB, layerId 0
nal-type 5 STSA, layerId 1
nal-type 1 PB, layerId 0
nal-type 5 STSA, layerId 1
nal-type 1 PB, layerId 0
...
nal-type 5 STSA, layerId 1
nal-type 1 PB, layerId 0
nal-type 5 STSA, layerId 1
nal-type 1 PB, layerId 0
nal-type 5 STSA, layerId 1
nal-type 1 PB, layerId 0
nal-type 5 STSA, layerId 1

 

 

————————————————————————————————————————————————————-

Remove NALU with Given nal_unit_type from H264 Elementary Stream

There are situations when it’s required to remove NALU with a specific nal_unit_type (e.g. NALUs belonging to an enhanced layer in scalable h264).

The script RemoveNalTypeInH264Stream.py is tailored to remove NALUs with given nal_unit_type.

 

Usage:

RemoveNalTypeInH264Stream.py    input.h264   nal_unit_type   output.h264

 

Example [remove NALUs with user-defined nal_unit_type=0x1F (31 in decimal)]:

python RemoveNalTypeInH264Stream.py   tomoishicheck.h264    31    out.h264
Nal units to remove 2223

23 Responses

  1. certainly like your web site however you need to check the spelling on several of your posts. A number of them are rife with spelling issues and I find it very troublesome to inform the truth nevertheless I will certainly come again again.

  2. Its like you read my mind! You appear to know so much about this, like you wrote the book in it or something. I think that you can do with some pics to drive the message home a little bit, but other than that, this is magnificent blog. An excellent read. I’ll definitely be back.

  3. I have to express appreciation to this writer for rescuing me from this problem. Just after researching through the world-wide-web and obtaining recommendations that were not helpful, I thought my entire life was well over. Being alive devoid of the solutions to the problems you have fixed as a result of your entire report is a crucial case, as well as ones which might have adversely damaged my career if I had not encountered your blog. Your know-how and kindness in handling every item was very helpful. I’m not sure what I would have done if I had not discovered such a solution like this. I’m able to at this moment look ahead to my future. Thanks so much for your skilled and amazing guide. I will not think twice to propose your site to anyone who needs to have guide about this area.

  4. Fantastic beat ! I wish to apprentice while you amend your site, how can i subscribe for a blog site? The account aided me a acceptable deal. I had been a little bit acquainted of this your broadcast provided bright clear idea

  5. Nice post. I learn something more challenging on different blogs everyday. It will always be stimulating to read content from other writers and practice a little something from their store. I’d prefer to use some with the content on my blog whether you don’t mind. Natually I’ll give you a link on your web blog. Thanks for sharing.

  6. I like what you guys are up too. Such clever work and reporting! Carry on the excellent works guys I have incorporated you guys to my blogroll. I think it’ll improve the value of my site 🙂

  7. hello!,I really like your writing very so much! share we keep up a correspondence more approximately your article on AOL? I require an expert in this space to resolve my problem. Maybe that is you! Having a look forward to look you.

  8. Hi, i believe that i noticed you visited my website so i came to ?go back the choose?.I am attempting to to find things to improve my site!I assume its ok to use a few of your concepts!!

  9. I am not sure where you’re getting your information, but great topic. I needs to spend some time learning much more or understanding more. Thanks for magnificent information I was looking for this information for my mission.

  10. My brother recommended I might like this blog. He was entirely right. This post truly made my day. You cann’t imagine simply how much time I had spent for this info! Thanks!

  11. I think other site proprietors should take this site as an model, very clean and fantastic user friendly style and design, as well as the content. You are an expert in this topic!

  12. Great work! That is the kind of information that are supposed to be shared around the web. Disgrace on Google for now not positioning this publish upper! Come on over and talk over with my website . Thanks =)

  13. Keep up the fantastic piece of work, I read few blog posts on this site and I conceive that your website is very interesting and has sets of good information.

Leave a Reply

Your email address will not be published. Required fields are marked *