Skip to content

Commit

Permalink
Pack SPS and PPS in one STAP-A packet
Browse files Browse the repository at this point in the history
Chrome need SPS and PPS in one STAP-A, and
timestamp of STAP-A should be same with next
I-Frame. Otherwise Chrome will discard SPS and PPS
before receiving I frame.
  • Loading branch information
baiyufei authored and Sean-Der committed Jun 14, 2023
1 parent 3b47cb1 commit 3c1e1ed
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 5 deletions.
46 changes: 41 additions & 5 deletions codecs/h264_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ import (
)

// H264Payloader payloads H264 packets
type H264Payloader struct{}
type H264Payloader struct {
spsNalu, ppsNalu []byte
}

const (
stapaNALUType = 24
fuaNALUType = 28
fubNALUType = 29
stapaNALUType = 24
fuaNALUType = 28
fubNALUType = 29
spsNALUType = 7
ppsNALUType = 8
audNALUType = 9
fillerNALUType = 12

fuaHeaderSize = 2
stapaHeaderSize = 1
Expand All @@ -21,6 +27,8 @@ const (
naluRefIdcBitmask = 0x60
fuStartBitmask = 0x80
fuEndBitmask = 0x40

outputStapAHeader = 0x78
)

func annexbNALUStartCode() []byte { return []byte{0x00, 0x00, 0x00, 0x01} }
Expand Down Expand Up @@ -75,8 +83,36 @@ func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
naluType := nalu[0] & naluTypeBitmask
naluRefIdc := nalu[0] & naluRefIdcBitmask

if naluType == 9 || naluType == 12 {
switch {
case naluType == audNALUType || naluType == fillerNALUType:
return
case naluType == spsNALUType:
p.spsNalu = nalu
return
case naluType == ppsNALUType:
p.ppsNalu = nalu
return
case p.spsNalu != nil && p.ppsNalu != nil:
// Pack current NALU with SPS and PPS as STAP-A
spsLen := make([]byte, 2)
binary.BigEndian.PutUint16(spsLen, uint16(len(p.spsNalu)))

ppsLen := make([]byte, 2)
binary.BigEndian.PutUint16(ppsLen, uint16(len(p.ppsNalu)))

stapANalu := []byte{outputStapAHeader}
stapANalu = append(stapANalu, spsLen...)
stapANalu = append(stapANalu, p.spsNalu...)
stapANalu = append(stapANalu, ppsLen...)
stapANalu = append(stapANalu, p.ppsNalu...)
if len(stapANalu) <= int(mtu) {
out := make([]byte, len(stapANalu))
copy(out, stapANalu)
payloads = append(payloads, out)
}

p.spsNalu = nil
p.ppsNalu = nil
}

// Single NALU
Expand Down
23 changes: 23 additions & 0 deletions codecs/h264_packet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,26 @@ func TestH264PartitionHeadChecker_IsPartitionHead(t *testing.T) {
t.Fatal("fub end nalu must not be a partition head")
}
}

func TestH264Payloader_Payload_SPS_and_PPS_handling(t *testing.T) {
pck := H264Payloader{}
expected := [][]byte{
{0x78, 0x00, 0x03, 0x07, 0x00, 0x01, 0x00, 0x03, 0x08, 0x02, 0x03},
{0x05, 0x04, 0x05},
}

// When packetizing SPS and PPS are emitted with following NALU
res := pck.Payload(1500, []byte{0x07, 0x00, 0x01})
if len(res) != 0 {
t.Fatal("Generated payload should be empty")
}

res = pck.Payload(1500, []byte{0x08, 0x02, 0x03})
if len(res) != 0 {
t.Fatal("Generated payload should be empty")
}

if !reflect.DeepEqual(pck.Payload(1500, []byte{0x05, 0x04, 0x05}), expected) {
t.Fatal("SPS and PPS aren't packed together")
}
}

0 comments on commit 3c1e1ed

Please sign in to comment.