7

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
zinon
  • 4,427
  • 14
  • 70
  • 112

2 Answers2

10

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.

Bastian35022
  • 1,092
  • 1
  • 10
  • 18
  • Thank you very much! I'll develop it using Python too. I will try it soon and if I' ll have any question I'll pm you! – zinon Jul 31 '14 at 16:17
  • Can you please help me with the parsing? I need some more help to read the headers. The find() method does not really work in bitstreams... This site does not support pm... – zinon Sep 10 '14 at 14:52
  • I assume you opened the bytestream and it's in a variable called "data", and you have the position "pos" of the beginning of a nal unit. Then "data[pos]" would be the first byte of the nal unit header, and "data[pos+1]" the second byte of the nal unit header. You can then get the nal unit type using "ord(data[pos]) & int(0b01111110)", and the temporal id using "ord(data[pos+1]) & int(0b00000111)". the layer id would be a little more difficult, as it has bits in both bytes. – Bastian35022 Sep 10 '14 at 15:32
  • Thank you! Using & you mean the'+' character? Is there a way to iterate through the bytestream and read the sequential nal type units? – zinon Sep 11 '14 at 09:03
  • Using & i mean the '&' character ;) this is a bitwise operator. Let me explain in more detail: The first bit of the NAL unit header must always be zero and carries no information for you. the next 6 bits make up the nal unit type in the form of an unsigned integer, so if the first byte of the nal unit header is e.g. 0b01000011, you nal unit type is 0b100001 = 33 (2^5+2^0), and you get this number using the bitwise operation described above. One thing i forgot: you have to bitshift the whole thing to get the correct results. So just do "(ord(data[pos]) & int(0b01111110))>>1". – Bastian35022 Sep 11 '14 at 09:36
  • OK Thank you! I parse the bitstream file using s = BitStream(filename= ... ) Is there a way to iterate through this bitstream to get all the nal unit types of the included packets? – zinon Sep 11 '14 at 09:59
  • You are using the bitstring module? Then there should be a find method, of the base class of "BitStream", "Bits": https://pythonhosted.org/bitstring/constbitarray.html#bitstring.Bits . Make sure that you do a byte aligned search for the pattern nal unit seperator ('0x000001'). – Bastian35022 Sep 11 '14 at 10:26
  • And if you use the bitstring module anyway, you can make your life easier by using the read methods of 'BitStream's base class 'ConstBitStream': https://pythonhosted.org/bitstring/constbitstream.html#bitstring.ConstBitStream – Bastian35022 Sep 11 '14 at 10:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/61045/discussion-between-zinon-and-bastian35022). – zinon Sep 11 '14 at 10:37
  • Please join the chat room please! – zinon Sep 11 '14 at 11:14
  • Unfortunately I still haven't managed to read the nal unit type correctly. I edited my previous post above, including my code. Can you please help me more? Thank you in advance!! – zinon Sep 12 '14 at 12:53
  • Great!! Thanks a lot!! Is there a way to find the size of the packet, the POC number and the offset? – zinon Sep 15 '14 at 12:26
  • Size of the packet (in bits) would be pcks[1]-pcks[0]-24, as described above, or do you mean something different? – Bastian35022 Sep 15 '14 at 12:48
  • What do you mean by offset? As for the POC number, this information can be extracted from the slice header, but is not too simple. First, you have to parse the slice segment nal unit according to section 7.3.6.1 of the HEVC standard to get the value for slice_pic_order_cnt_lsb, and then you can calculate the POC according to section 8.3.1 of the HEVC standard. I didn't do this before, so i don't have detailed knowledge about this decoding process :( – Bastian35022 Sep 15 '14 at 13:00
  • OK for packet size! Thanks! Offset is a hex number for example 0x00000000 for first packet, 0x00000021 for 2nd, 0x00000093 for 3rd etc. Go to http://www.lengaur.com/parser/ and try my bitstream file. OK about slice segment. I'll try it. Thank you once again! – zinon Sep 15 '14 at 13:21
  • You get the offsets of the nal units in this format by converting the byte positions of the nal unit separators to hex strings: hex(pcks[i]/8). The positions on the webside are the positions of access units. You can usually get these by searching for 0x00000001 instead of 0x000001, but i am not too sure myself, if this is completely standard conforming. – Bastian35022 Sep 15 '14 at 17:43
  • Thanks once again! Do you know if there is a way to find the arrival time of each frame or packet? – zinon Sep 16 '14 at 13:01
  • If you mean the time when the packet or frame arrived over the network etc., then this information is not included in the HEVC bitstream. It is only dealing with video compression, not transport. For this, you'd need to use tools like tcpdump or tshark i guess, but i'm not too familiar with those, unfortunately :( – Bastian35022 Sep 16 '14 at 13:18
  • No, I mean time of encoding for each frame. If a decode an str stream, I get decoded time for each frame. Is there a corresponding field for encoding time? – zinon Sep 16 '14 at 13:32
  • Ah okay. Not that i know of. If there exists something in the Bitstream, i am pretty sure it would in an SEI message (Annex E of the standard), and thus not mandatory, so usually not part of the bitstream. – Bastian35022 Sep 16 '14 at 13:41
  • Yes, I think is a computation using pic_dpb_output_delay. OK, thank you! – zinon Sep 16 '14 at 14:26
0

You can try GStreamer. They have a plugin for parsing H.265/HEVC. Just that the plugin is fairly new still so I'm not certain how stable it is right now.

You can get more details here.

wontonsoup
  • 132
  • 5