perf: 优化后端性能与直播延迟,包含数据库WAL模式、流媒体写缓冲、转码 Preset 优化、聊天锁和指标采集优化,以及前端自动追帧功能
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nareix/joy4/av"
|
||||
"github.com/nareix/joy4/av/avutil"
|
||||
"github.com/nareix/joy4/av/pubsub"
|
||||
"github.com/nareix/joy4/format"
|
||||
@@ -82,6 +84,23 @@ func (w writeFlusher) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type bufferedWriteFlusher struct {
|
||||
bufw *bufio.Writer
|
||||
httpFlusher http.Flusher
|
||||
}
|
||||
|
||||
func (w *bufferedWriteFlusher) Write(p []byte) (n int, err error) {
|
||||
return w.bufw.Write(p)
|
||||
}
|
||||
|
||||
func (w *bufferedWriteFlusher) Flush() error {
|
||||
if err := w.bufw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
w.httpFlusher.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRTMPServer creates and initializes a new media server
|
||||
func NewRTMPServer() *RTMPServer {
|
||||
s := &RTMPServer{
|
||||
@@ -235,13 +254,47 @@ func (s *RTMPServer) HandleHTTPFLV(c *gin.Context) {
|
||||
c.Status(http.StatusOK)
|
||||
flusher.Flush()
|
||||
|
||||
muxer := flv.NewMuxerWriteFlusher(writeFlusher{
|
||||
// Coalesce the 3 internal write calls of WriteTag using a 4KB bufio.Writer
|
||||
bufWriter := bufio.NewWriterSize(c.Writer, 4096)
|
||||
bwf := &bufferedWriteFlusher{
|
||||
bufw: bufWriter,
|
||||
httpFlusher: flusher,
|
||||
Writer: c.Writer,
|
||||
})
|
||||
}
|
||||
|
||||
muxer := flv.NewMuxerWriteFlusher(bwf)
|
||||
cursor := q.Latest()
|
||||
|
||||
if err := avutil.CopyFile(muxer, cursor); err != nil && err != io.EOF {
|
||||
// Write header first
|
||||
streams, err := cursor.Streams()
|
||||
if err != nil {
|
||||
monitor.Errorf("HTTP-FLV failed to get cursor streams: %v", err)
|
||||
return
|
||||
}
|
||||
if err = muxer.WriteHeader(streams); err != nil {
|
||||
monitor.Errorf("HTTP-FLV failed to write header: %v", err)
|
||||
return
|
||||
}
|
||||
if err = bwf.Flush(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Read and write packet loop with per-packet flushing for low latency
|
||||
for {
|
||||
var pkt av.Packet
|
||||
pkt, err = cursor.ReadPacket()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if err = muxer.WritePacket(pkt); err != nil {
|
||||
break
|
||||
}
|
||||
// Flush immediately so the frame is sent to the client (grouped write syscall)
|
||||
if err = bwf.Flush(); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, "broken pipe") || strings.Contains(errStr, "connection reset by peer") {
|
||||
monitor.Infof("HTTP-FLV viewer disconnected: %s", streamPath)
|
||||
@@ -299,10 +352,11 @@ func (s *RTMPServer) startVariantTranscoders(roomID string) {
|
||||
"ffmpeg",
|
||||
"-nostdin",
|
||||
"-loglevel", "error",
|
||||
"-fflags", "nobuffer",
|
||||
"-i", inputURL,
|
||||
"-vf", "scale="+profile.scale+":force_original_aspect_ratio=decrease",
|
||||
"-c:v", "libx264",
|
||||
"-preset", "veryfast",
|
||||
"-preset", "ultrafast",
|
||||
"-tune", "zerolatency",
|
||||
"-g", "48",
|
||||
"-keyint_min", "48",
|
||||
|
||||
Reference in New Issue
Block a user