github.com/pion/webrtc/v4@v4.0.1/pkg/media/h264reader/h264reader.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  // Package h264reader implements a H264 Annex-B Reader
     5  package h264reader
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"io"
    11  )
    12  
    13  // H264Reader reads data from stream and constructs h264 nal units
    14  type H264Reader struct {
    15  	stream                      io.Reader
    16  	nalBuffer                   []byte
    17  	countOfConsecutiveZeroBytes int
    18  	nalPrefixParsed             bool
    19  	readBuffer                  []byte
    20  	tmpReadBuf                  []byte
    21  }
    22  
    23  var (
    24  	errNilReader           = errors.New("stream is nil")
    25  	errDataIsNotH264Stream = errors.New("data is not a H264 bitstream")
    26  )
    27  
    28  // NewReader creates new H264Reader
    29  func NewReader(in io.Reader) (*H264Reader, error) {
    30  	if in == nil {
    31  		return nil, errNilReader
    32  	}
    33  
    34  	reader := &H264Reader{
    35  		stream:          in,
    36  		nalBuffer:       make([]byte, 0),
    37  		nalPrefixParsed: false,
    38  		readBuffer:      make([]byte, 0),
    39  		tmpReadBuf:      make([]byte, 4096),
    40  	}
    41  
    42  	return reader, nil
    43  }
    44  
    45  // NAL H.264 Network Abstraction Layer
    46  type NAL struct {
    47  	PictureOrderCount uint32
    48  
    49  	// NAL header
    50  	ForbiddenZeroBit bool
    51  	RefIdc           uint8
    52  	UnitType         NalUnitType
    53  
    54  	Data []byte // header byte + rbsp
    55  }
    56  
    57  func (reader *H264Reader) read(numToRead int) (data []byte, e error) {
    58  	for len(reader.readBuffer) < numToRead {
    59  		n, err := reader.stream.Read(reader.tmpReadBuf)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		if n == 0 {
    64  			break
    65  		}
    66  		reader.readBuffer = append(reader.readBuffer, reader.tmpReadBuf[0:n]...)
    67  	}
    68  	var numShouldRead int
    69  	if numToRead <= len(reader.readBuffer) {
    70  		numShouldRead = numToRead
    71  	} else {
    72  		numShouldRead = len(reader.readBuffer)
    73  	}
    74  	data = reader.readBuffer[0:numShouldRead]
    75  	reader.readBuffer = reader.readBuffer[numShouldRead:]
    76  	return data, nil
    77  }
    78  
    79  func (reader *H264Reader) bitStreamStartsWithH264Prefix() (prefixLength int, e error) {
    80  	nalPrefix3Bytes := []byte{0, 0, 1}
    81  	nalPrefix4Bytes := []byte{0, 0, 0, 1}
    82  
    83  	prefixBuffer, e := reader.read(4)
    84  	if e != nil {
    85  		return
    86  	}
    87  
    88  	n := len(prefixBuffer)
    89  
    90  	if n == 0 {
    91  		return 0, io.EOF
    92  	}
    93  
    94  	if n < 3 {
    95  		return 0, errDataIsNotH264Stream
    96  	}
    97  
    98  	nalPrefix3BytesFound := bytes.Equal(nalPrefix3Bytes, prefixBuffer[:3])
    99  	if n == 3 {
   100  		if nalPrefix3BytesFound {
   101  			return 0, io.EOF
   102  		}
   103  		return 0, errDataIsNotH264Stream
   104  	}
   105  
   106  	// n == 4
   107  	if nalPrefix3BytesFound {
   108  		reader.nalBuffer = append(reader.nalBuffer, prefixBuffer[3])
   109  		return 3, nil
   110  	}
   111  
   112  	nalPrefix4BytesFound := bytes.Equal(nalPrefix4Bytes, prefixBuffer)
   113  	if nalPrefix4BytesFound {
   114  		return 4, nil
   115  	}
   116  	return 0, errDataIsNotH264Stream
   117  }
   118  
   119  // NextNAL reads from stream and returns then next NAL,
   120  // and an error if there is incomplete frame data.
   121  // Returns all nil values when no more NALs are available.
   122  func (reader *H264Reader) NextNAL() (*NAL, error) {
   123  	if !reader.nalPrefixParsed {
   124  		_, err := reader.bitStreamStartsWithH264Prefix()
   125  		if err != nil {
   126  			return nil, err
   127  		}
   128  
   129  		reader.nalPrefixParsed = true
   130  	}
   131  
   132  	for {
   133  		buffer, err := reader.read(1)
   134  		if err != nil {
   135  			break
   136  		}
   137  
   138  		n := len(buffer)
   139  
   140  		if n != 1 {
   141  			break
   142  		}
   143  		readByte := buffer[0]
   144  		nalFound := reader.processByte(readByte)
   145  		if nalFound {
   146  			nal := newNal(reader.nalBuffer)
   147  			nal.parseHeader()
   148  			if nal.UnitType == NalUnitTypeSEI {
   149  				reader.nalBuffer = nil
   150  				continue
   151  			}
   152  			break
   153  		}
   154  
   155  		reader.nalBuffer = append(reader.nalBuffer, readByte)
   156  	}
   157  
   158  	if len(reader.nalBuffer) == 0 {
   159  		return nil, io.EOF
   160  	}
   161  
   162  	nal := newNal(reader.nalBuffer)
   163  	reader.nalBuffer = nil
   164  	nal.parseHeader()
   165  
   166  	return nal, nil
   167  }
   168  
   169  func (reader *H264Reader) processByte(readByte byte) (nalFound bool) {
   170  	nalFound = false
   171  
   172  	switch readByte {
   173  	case 0:
   174  		reader.countOfConsecutiveZeroBytes++
   175  	case 1:
   176  		if reader.countOfConsecutiveZeroBytes >= 2 {
   177  			countOfConsecutiveZeroBytesInPrefix := 2
   178  			if reader.countOfConsecutiveZeroBytes > 2 {
   179  				countOfConsecutiveZeroBytesInPrefix = 3
   180  			}
   181  
   182  			if nalUnitLength := len(reader.nalBuffer) - countOfConsecutiveZeroBytesInPrefix; nalUnitLength > 0 {
   183  				reader.nalBuffer = reader.nalBuffer[0:nalUnitLength]
   184  				nalFound = true
   185  			}
   186  		}
   187  
   188  		reader.countOfConsecutiveZeroBytes = 0
   189  	default:
   190  		reader.countOfConsecutiveZeroBytes = 0
   191  	}
   192  
   193  	return nalFound
   194  }
   195  
   196  func newNal(data []byte) *NAL {
   197  	return &NAL{PictureOrderCount: 0, ForbiddenZeroBit: false, RefIdc: 0, UnitType: NalUnitTypeUnspecified, Data: data}
   198  }
   199  
   200  func (h *NAL) parseHeader() {
   201  	firstByte := h.Data[0]
   202  	h.ForbiddenZeroBit = (((firstByte & 0x80) >> 7) == 1) // 0x80 = 0b10000000
   203  	h.RefIdc = (firstByte & 0x60) >> 5                    // 0x60 = 0b01100000
   204  	h.UnitType = NalUnitType((firstByte & 0x1F) >> 0)     // 0x1F = 0b00011111
   205  }