Parsing hevc bitstream

zinon picture zinon · Jul 18, 2014 · Viewed 11.5k times · Source

Is there a way to parse an HEVC bitstream file?

I want to be able to create a new bitstream file having selected nal unit packets selected from the original bitstream file.

Edit: I inserted my code. Please find my bitstream file here.

#library for searching in a string
import re

#library to keep dictionary order
import collections
import bitstring
from bitstring import BitStream, BitArray, ConstBitStream, pack
from bitstring import ByteStore, offsetcopy

#read bitstream file
s = BitStream(filename='11LTCCA_560x416_50Hz_8b_P420_GOP8_IP48_200frms_QP28.HEVC.str')

#find no of packets
pcks = list(s.findall('0x000001', bytealigned=True))

print len(pcks)

#set the current position, in the beginning of the nal unit.
s.pos =pcks[0]-8
print s.pos

#find the number of bits of first nal packet
no_p = pcks[1]-pcks[0]


forbidden_zero_bit = s.read(1)
nal_unit_type = s.read('uint:6')

# go to the beginning of the second nal unit
s.read(no_p)
# print nal unit type of the 1st packet
print nal_unit_type

no_p = pcks[2]-pcks[1]
s.pos = pcks[1]-8
print s.pos
forbidden_zero_bit = s.read(1)
nal_unit_type = s.read('uint:6')
s.read(no_p)
print nal_unit_type

Answer

Bastian35022 picture Bastian35022 · Jul 28, 2014

If all you want to do is take some nal unit packets (e.g. depending on layer id and temporal id), and you don't need to modify the VPS, SPS, PPS, Slice Header etc., then you can also easily implement this yourself:

The corresponding syntax is stated in the Annex B "Byte Stream Format" of the HEVC standard.

In short:

  1. Search the bitstream file for the pattern 0x000001, which separates all the nal units. Additionally, there can be a 0x00 byte before this pattern, if the next nal unit is the first nal unit of an access unit (access unit = all nal units for decoding a whole frame).

  2. Read the nal unit header according to section 7.3.1.2 of the HEVC standard and keep/delete the nal units based on whatever criteria you want. Make sure you keep the parameter sets (nal unit types 32, 33 and 34 according to Table 7-1 of the HEVC standard).

  3. Assemble all the nal units in a new file and make sure you always have the 0x000001 sequence inbetween.

I once did something similar using Python, which worked pretty well. If you want to make reading the nal unit headers easier, use the bitstring module. If you want to do this and have more detailed questions, you can pm me for help if you want to.

Edit: Regarding the code you posted: Why do you put "-8" when assigning the position in the BitStream object (s.pos =pcks[0]-8 and s.pos = pcks[1]-8)? This should be +24 (24 bits = 3 bytes = length of the nal unit separator 0x000001), to start reading after the separator to get the nal unit. However, you have to take this into account when reading data: no_p = pcks[1]-pcks[0] should be no_p = pcks[1]-pcks[0]-24, because you start reading after the nal unit separator.

If you got confused that the first found position (pcks[0]) is 8, not 0: Before each nal unit separator, there can be an arbitrary number of zero-bytes, according to the annex B of the HEVC standard. Usually, there is always one zero-byte before each access unit.