mirror of
https://github.com/livekit/livekit.git
synced 2026-10-23 23:52:57 +00:00
Add silent frame for pcmu/a (#4258)
* Add silent frame for pcmu/a * use red * solve comment
This commit is contained in:
1 parent
0c33b8c671
commit
01bd966ff8
1 file changed
+89
-17
+89
-17
@@ -139,6 +139,56 @@ var (
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// PCMU (G.711 µ-law) silence frame - 0xff represents zero amplitude
|
||||
// 160 samples = 20ms at 8kHz sample rate
|
||||
PCMUSilenceFrame = []byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
}
|
||||
|
||||
// PCMA (G.711 A-law) silence frame - 0xd5 represents zero amplitude
|
||||
// 160 samples = 20ms at 8kHz sample rate
|
||||
PCMASilenceFrame = []byte{
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
|
||||
}
|
||||
|
||||
dummyAbsSendTimeExt, _ = rtp.NewAbsSendTimeExtension(mono.Now()).Marshal()
|
||||
dummyTransportCCExt, _ = rtp.TransportCCExtension{TransportSequence: 12345}.Marshal()
|
||||
)
|
||||
@@ -1697,9 +1747,13 @@ func (d *DownTrack) writeBlankFrameRTP(duration float32, generation uint32) chan
|
||||
var getBlankFrame func(bool) ([]byte, error)
|
||||
switch mimeType {
|
||||
case mime.MimeTypeOpus:
|
||||
getBlankFrame = d.getOpusBlankFrame
|
||||
getBlankFrame = d.getAudioBlankFrameFunc(OpusSilenceFrame)
|
||||
case mime.MimeTypeRED:
|
||||
getBlankFrame = d.getOpusRedBlankFrame
|
||||
case mime.MimeTypePCMU:
|
||||
getBlankFrame = d.getAudioBlankFrameFunc(PCMUSilenceFrame)
|
||||
case mime.MimeTypePCMA:
|
||||
getBlankFrame = d.getAudioBlankFrameFunc(PCMASilenceFrame)
|
||||
case mime.MimeTypeVP8:
|
||||
getBlankFrame = d.getVP8BlankFrame
|
||||
case mime.MimeTypeH264:
|
||||
@@ -1710,7 +1764,8 @@ func (d *DownTrack) writeBlankFrameRTP(duration float32, generation uint32) chan
|
||||
}
|
||||
|
||||
frameRate := uint32(30)
|
||||
if mimeType == mime.MimeTypeOpus || mimeType == mime.MimeTypeRED {
|
||||
if mimeType == mime.MimeTypeOpus || mimeType == mime.MimeTypeRED ||
|
||||
mimeType == mime.MimeTypePCMU || mimeType == mime.MimeTypePCMA {
|
||||
frameRate = 50
|
||||
}
|
||||
|
||||
@@ -1801,15 +1856,17 @@ func (d *DownTrack) maybeAddTrailer(buf []byte) int {
|
||||
return len(d.params.Trailer)
|
||||
}
|
||||
|
||||
func (d *DownTrack) getOpusBlankFrame(_frameEndNeeded bool) ([]byte, error) {
|
||||
func (d *DownTrack) getAudioBlankFrameFunc(silentPayload []byte) func(_frameEndNeeded bool) ([]byte, error) {
|
||||
// silence frame
|
||||
// Used shortly after muting to ensure residual noise does not keep
|
||||
// generating noise at the decoder after the stream is stopped
|
||||
// i. e. comfort noise generation actually not producing something comfortable.
|
||||
payload := make([]byte, 1000)
|
||||
copy(payload[0:], OpusSilenceFrame)
|
||||
trailerLen := d.maybeAddTrailer(payload[len(OpusSilenceFrame):])
|
||||
return payload[:len(OpusSilenceFrame)+trailerLen], nil
|
||||
return func(_frameEndNeeded bool) ([]byte, error) {
|
||||
payload := make([]byte, 1000)
|
||||
copy(payload[0:], silentPayload)
|
||||
trailerLen := d.maybeAddTrailer(payload[len(silentPayload):])
|
||||
return payload[:len(silentPayload)+trailerLen], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DownTrack) getOpusRedBlankFrame(_frameEndNeeded bool) ([]byte, error) {
|
||||
@@ -2443,8 +2500,8 @@ func (d *DownTrack) sendPaddingOnMute() {
|
||||
|
||||
if d.kind == webrtc.RTPCodecTypeVideo {
|
||||
d.sendPaddingOnMuteForVideo()
|
||||
} else if d.Mime() == mime.MimeTypeOpus {
|
||||
d.sendSilentFrameOnMuteForOpus()
|
||||
} else {
|
||||
d.sendSilentFrameOnMuteForAudio()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2462,7 +2519,29 @@ func (d *DownTrack) sendPaddingOnMuteForVideo() {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DownTrack) sendSilentFrameOnMuteForOpus() {
|
||||
func (d *DownTrack) sendSilentFrameOnMuteForAudio() {
|
||||
var (
|
||||
payload []byte
|
||||
err error
|
||||
)
|
||||
switch d.Mime() {
|
||||
case mime.MimeTypeOpus:
|
||||
payload, err = d.getAudioBlankFrameFunc(OpusSilenceFrame)(false)
|
||||
case mime.MimeTypeRED:
|
||||
payload, err = d.getOpusRedBlankFrame(false)
|
||||
case mime.MimeTypePCMU:
|
||||
payload, err = d.getAudioBlankFrameFunc(PCMUSilenceFrame)(false)
|
||||
case mime.MimeTypePCMA:
|
||||
payload, err = d.getAudioBlankFrameFunc(PCMASilenceFrame)(false)
|
||||
default:
|
||||
d.params.Logger.Infow("unsupported mime type for silent frame on mute", "mimeType", d.Mime())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
d.params.Logger.Warnw("could not get blank frame", err)
|
||||
return
|
||||
}
|
||||
|
||||
frameRate := uint32(50)
|
||||
frameDuration := time.Duration(1000/frameRate) * time.Millisecond
|
||||
numFrames := frameRate * uint32(maxPaddingOnMuteDuration/time.Second)
|
||||
@@ -2491,13 +2570,6 @@ func (d *DownTrack) sendSilentFrameOnMuteForOpus() {
|
||||
SSRC: d.ssrc,
|
||||
}
|
||||
d.addDummyExtensions(hdr)
|
||||
|
||||
payload, err := d.getOpusBlankFrame(false)
|
||||
if err != nil {
|
||||
d.params.Logger.Warnw("could not get blank frame", err)
|
||||
return
|
||||
}
|
||||
|
||||
headerSize := hdr.MarshalSize()
|
||||
d.rtpStats.Update(
|
||||
mono.UnixNano(),
|
||||
|
||||
Reference in new issue
Block a user