mirror of
https://github.com/pion/webrtc.git
synced 2025-10-26 16:50:34 +08:00
Add H264 send/receive and packetization support
This commit is contained in:
committed by
John R. Bradley
parent
dfd08a5ac4
commit
cfba14cfea
@@ -32,6 +32,8 @@ func CreatePipeline(codec webrtc.TrackType) *Pipeline {
|
|||||||
pipelineStr += ", payload=96, encoding-name=OPUS ! rtpopusdepay ! decodebin ! autoaudiosink"
|
pipelineStr += ", payload=96, encoding-name=OPUS ! rtpopusdepay ! decodebin ! autoaudiosink"
|
||||||
case webrtc.VP9:
|
case webrtc.VP9:
|
||||||
pipelineStr += " ! rtpvp9depay ! decodebin ! autovideosink"
|
pipelineStr += " ! rtpvp9depay ! decodebin ! autovideosink"
|
||||||
|
case webrtc.H264:
|
||||||
|
pipelineStr += " ! rtph264depay ! decodebin ! autovideosink"
|
||||||
default:
|
default:
|
||||||
panic("Unhandled codec " + codec.String())
|
panic("Unhandled codec " + codec.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ func CreatePipeline(codec webrtc.TrackType, in chan<- webrtc.RTCSample) *Pipelin
|
|||||||
pipelineStr = "videotestsrc ! vp8enc ! " + pipelineStr
|
pipelineStr = "videotestsrc ! vp8enc ! " + pipelineStr
|
||||||
case webrtc.VP9:
|
case webrtc.VP9:
|
||||||
pipelineStr = "videotestsrc ! vp9enc ! " + pipelineStr
|
pipelineStr = "videotestsrc ! vp9enc ! " + pipelineStr
|
||||||
|
case webrtc.H264:
|
||||||
|
pipelineStr = "videotestsrc ! video/x-raw,format=I420 ! x264enc bframes=0 speed-preset=veryfast key-int-max=60 ! video/x-h264,stream-format=byte-stream ! " + pipelineStr
|
||||||
case webrtc.Opus:
|
case webrtc.Opus:
|
||||||
pipelineStr = "audiotestsrc ! opusenc ! " + pipelineStr
|
pipelineStr = "audiotestsrc ! opusenc ! " + pipelineStr
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ type SessionBuilder struct {
|
|||||||
Tracks []*SessionBuilderTrack
|
Tracks []*SessionBuilderTrack
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseSessionDescription generates a default SDP response that is ice-lite, initiates the DTLS session and supports VP8, VP9 and Opus
|
// BaseSessionDescription generates a default SDP response that is ice-lite, initiates the DTLS session and
|
||||||
|
// supports VP8, VP9, H264 and Opus
|
||||||
func BaseSessionDescription(b *SessionBuilder) *SessionDescription {
|
func BaseSessionDescription(b *SessionBuilder) *SessionDescription {
|
||||||
addMediaCandidates := func(m *MediaDescription) *MediaDescription {
|
addMediaCandidates := func(m *MediaDescription) *MediaDescription {
|
||||||
m.Attributes = append(m.Attributes, b.Candidates...)
|
m.Attributes = append(m.Attributes, b.Candidates...)
|
||||||
@@ -49,7 +50,7 @@ func BaseSessionDescription(b *SessionBuilder) *SessionDescription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
videoMediaDescription := &MediaDescription{
|
videoMediaDescription := &MediaDescription{
|
||||||
MediaName: "video 9 RTP/SAVPF 96 98",
|
MediaName: "video 9 RTP/SAVPF 96 98 100",
|
||||||
ConnectionData: "IN IP4 127.0.0.1",
|
ConnectionData: "IN IP4 127.0.0.1",
|
||||||
Attributes: []string{
|
Attributes: []string{
|
||||||
"setup:active",
|
"setup:active",
|
||||||
@@ -63,6 +64,8 @@ func BaseSessionDescription(b *SessionBuilder) *SessionDescription {
|
|||||||
"rtcp-rsize",
|
"rtcp-rsize",
|
||||||
"rtpmap:96 VP8/90000",
|
"rtpmap:96 VP8/90000",
|
||||||
"rtpmap:98 VP9/90000",
|
"rtpmap:98 VP9/90000",
|
||||||
|
"rtpmap:100 H264/90000",
|
||||||
|
"fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
123
pkg/rtp/codecs/h264_packet.go
Normal file
123
pkg/rtp/codecs/h264_packet.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package codecs
|
||||||
|
|
||||||
|
// H264Payloader payloads H264 packets
|
||||||
|
type H264Payloader struct{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
fuaHeaderSize = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func emitNalus(nals []byte, emit func([]byte)) {
|
||||||
|
nextInd := func(nalu []byte, start int) (indStart int, indLen int) {
|
||||||
|
zeroCount := 0
|
||||||
|
|
||||||
|
for i, b := range nalu[start:] {
|
||||||
|
if b == 0 {
|
||||||
|
zeroCount++
|
||||||
|
continue
|
||||||
|
} else if b == 1 {
|
||||||
|
if zeroCount >= 2 {
|
||||||
|
return start + i - zeroCount, zeroCount + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zeroCount = 0
|
||||||
|
}
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
nextIndStart, nextIndLen := nextInd(nals, 0)
|
||||||
|
if nextIndStart == -1 {
|
||||||
|
emit(nals)
|
||||||
|
} else {
|
||||||
|
for nextIndStart != -1 {
|
||||||
|
prevStart := nextIndStart + nextIndLen
|
||||||
|
nextIndStart, nextIndLen = nextInd(nals, prevStart)
|
||||||
|
if nextIndStart != -1 {
|
||||||
|
emit(nals[prevStart:nextIndStart])
|
||||||
|
} else {
|
||||||
|
// Emit until end of stream, no end indicator found
|
||||||
|
emit(nals[prevStart:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload fragments a H264 packet across one or more byte arrays
|
||||||
|
func (p *H264Payloader) Payload(mtu int, payload []byte) [][]byte {
|
||||||
|
|
||||||
|
var payloads [][]byte
|
||||||
|
|
||||||
|
emitNalus(payload, func(nalu []byte) {
|
||||||
|
naluType := nalu[0] & 0x1F
|
||||||
|
naluRefIdc := nalu[0] & 0x60
|
||||||
|
|
||||||
|
if naluType == 9 || naluType == 12 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single NALU
|
||||||
|
if len(nalu) <= mtu {
|
||||||
|
out := make([]byte, len(nalu))
|
||||||
|
copy(out, nalu)
|
||||||
|
payloads = append(payloads, out)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FU-A
|
||||||
|
maxFragmentSize := mtu - fuaHeaderSize
|
||||||
|
|
||||||
|
// The FU payload consists of fragments of the payload of the fragmented
|
||||||
|
// NAL unit so that if the fragmentation unit payloads of consecutive
|
||||||
|
// FUs are sequentially concatenated, the payload of the fragmented NAL
|
||||||
|
// unit can be reconstructed. The NAL unit type octet of the fragmented
|
||||||
|
// NAL unit is not included as such in the fragmentation unit payload,
|
||||||
|
// but rather the information of the NAL unit type octet of the
|
||||||
|
// fragmented NAL unit is conveyed in the F and NRI fields of the FU
|
||||||
|
// indicator octet of the fragmentation unit and in the type field of
|
||||||
|
// the FU header. An FU payload MAY have any number of octets and MAY
|
||||||
|
// be empty.
|
||||||
|
|
||||||
|
naluData := nalu
|
||||||
|
// According to the RFC, the first octet is skipped due to redundant information
|
||||||
|
naluDataIndex := 1
|
||||||
|
naluDataLength := len(nalu) - naluDataIndex
|
||||||
|
naluDataRemaining := naluDataLength
|
||||||
|
|
||||||
|
for naluDataRemaining > 0 {
|
||||||
|
currentFragmentSize := min(maxFragmentSize, naluDataRemaining)
|
||||||
|
out := make([]byte, fuaHeaderSize+currentFragmentSize)
|
||||||
|
|
||||||
|
// +---------------+
|
||||||
|
// |0|1|2|3|4|5|6|7|
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// |F|NRI| Type |
|
||||||
|
// +---------------+
|
||||||
|
out[0] = 28
|
||||||
|
out[0] |= naluRefIdc
|
||||||
|
|
||||||
|
// +---------------+
|
||||||
|
//|0|1|2|3|4|5|6|7|
|
||||||
|
//+-+-+-+-+-+-+-+-+
|
||||||
|
//|S|E|R| Type |
|
||||||
|
//+---------------+
|
||||||
|
|
||||||
|
out[1] = naluType
|
||||||
|
if naluDataRemaining == naluDataLength {
|
||||||
|
// Set start bit
|
||||||
|
out[1] |= 1 << 7
|
||||||
|
} else if naluDataRemaining-currentFragmentSize == 0 {
|
||||||
|
// Set end bit
|
||||||
|
out[1] |= 1 << 6
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(out[fuaHeaderSize:], naluData[naluDataIndex:naluDataIndex+currentFragmentSize])
|
||||||
|
payloads = append(payloads, out)
|
||||||
|
|
||||||
|
naluDataRemaining -= currentFragmentSize
|
||||||
|
naluDataIndex += currentFragmentSize
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return payloads
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ type TrackType int
|
|||||||
const (
|
const (
|
||||||
VP8 TrackType = iota + 1
|
VP8 TrackType = iota + 1
|
||||||
VP9
|
VP9
|
||||||
|
H264
|
||||||
Opus
|
Opus
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@ func (t TrackType) String() string {
|
|||||||
return "VP8"
|
return "VP8"
|
||||||
case VP9:
|
case VP9:
|
||||||
return "VP9"
|
return "VP9"
|
||||||
|
case H264:
|
||||||
|
return "H264"
|
||||||
case Opus:
|
case Opus:
|
||||||
return "Opus"
|
return "Opus"
|
||||||
default:
|
default:
|
||||||
@@ -127,7 +130,7 @@ func (r *RTCPeerConnection) CreateAnswer() error {
|
|||||||
// This function returns a channel to push buffers on, and an error if the channel can't be added
|
// This function returns a channel to push buffers on, and an error if the channel can't be added
|
||||||
// Closing the channel ends this stream
|
// Closing the channel ends this stream
|
||||||
func (r *RTCPeerConnection) AddTrack(mediaType TrackType, clockRate uint32) (samples chan<- RTCSample, err error) {
|
func (r *RTCPeerConnection) AddTrack(mediaType TrackType, clockRate uint32) (samples chan<- RTCSample, err error) {
|
||||||
if mediaType != VP8 && mediaType != Opus {
|
if mediaType != VP8 && mediaType != H264 && mediaType != Opus {
|
||||||
panic("TODO Discarding packet, need media parsing")
|
panic("TODO Discarding packet, need media parsing")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +205,8 @@ func (r *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buf
|
|||||||
codec = VP9
|
codec = VP9
|
||||||
case "opus":
|
case "opus":
|
||||||
codec = Opus
|
codec = Opus
|
||||||
|
case "H264":
|
||||||
|
codec = H264
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Codec %s in not supported by pion-WebRTC \n", codecStr)
|
fmt.Printf("Codec %s in not supported by pion-WebRTC \n", codecStr)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user