Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49773a1ece | |||
| c97a48a73f | |||
| e03231ebb4 | |||
| 649525a842 | |||
| d411c1a25c | |||
| 2f0bcf4ae0 | |||
| 831c504cab | |||
| 12925a6bc5 |
+6
-1
@@ -85,10 +85,15 @@ var wsHandlers = make(map[string]WSHandler)
|
|||||||
|
|
||||||
func streamsHandler(w http.ResponseWriter, r *http.Request) {
|
func streamsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
src := r.URL.Query().Get("src")
|
src := r.URL.Query().Get("src")
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = src
|
||||||
|
}
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case "PUT":
|
case "PUT":
|
||||||
streams.New(src, src)
|
streams.New(name, src)
|
||||||
return
|
return
|
||||||
case "DELETE":
|
case "DELETE":
|
||||||
streams.Delete(src)
|
streams.Delete(src)
|
||||||
|
|||||||
+44
-24
@@ -15,15 +15,14 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc {
|
|||||||
sps, pps := GetParameterSet(track.Codec.FmtpLine)
|
sps, pps := GetParameterSet(track.Codec.FmtpLine)
|
||||||
ps := EncodeAVC(sps, pps)
|
ps := EncodeAVC(sps, pps)
|
||||||
|
|
||||||
var buffer []byte
|
buf := make([]byte, 0, 512*1024) // 512K
|
||||||
|
|
||||||
return func(push streamer.WriterFunc) streamer.WriterFunc {
|
return func(push streamer.WriterFunc) streamer.WriterFunc {
|
||||||
return func(packet *rtp.Packet) error {
|
return func(packet *rtp.Packet) error {
|
||||||
//nalUnitType := packet.Payload[0] & 0x1F
|
|
||||||
//fmt.Printf(
|
//fmt.Printf(
|
||||||
// "[RTP] codec: %s, nalu: %2d, size: %6d, ts: %10d, pt: %2d, ssrc: %d, seq: %d\n",
|
// "[RTP] codec: %s, nalu: %2d, size: %6d, ts: %10d, pt: %2d, ssrc: %d, seq: %d, %v\n",
|
||||||
// track.Codec.Name, nalUnitType, len(packet.Payload), packet.Timestamp,
|
// track.Codec.Name, packet.Payload[0]&0x1F, len(packet.Payload), packet.Timestamp,
|
||||||
// packet.PayloadType, packet.SSRC, packet.SequenceNumber,
|
// packet.PayloadType, packet.SSRC, packet.SequenceNumber, packet.Marker,
|
||||||
//)
|
//)
|
||||||
|
|
||||||
payload, err := depack.Unmarshal(packet.Payload)
|
payload, err := depack.Unmarshal(packet.Payload)
|
||||||
@@ -31,31 +30,52 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ffmpeg with `-tune zerolatency` enable option `-x264opts sliced-threads=1`
|
// Fix TP-Link Tapo TC70: sends SPS and PPS with packet.Marker = true
|
||||||
// and every NALU will be sliced to multiple NALUs
|
if packet.Marker {
|
||||||
|
switch NALUType(payload) {
|
||||||
|
case NALUTypeSPS, NALUTypePPS:
|
||||||
|
buf = append(buf, payload...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
switch NALUType(payload) {
|
||||||
|
case NALUTypeIFrame:
|
||||||
|
// fix IFrame without SPS,PPS
|
||||||
|
buf = append(buf, ps...)
|
||||||
|
case NALUTypeSEI:
|
||||||
|
// fix ffmpeg with transcoding first frame
|
||||||
|
i := int(4 + binary.BigEndian.Uint32(payload))
|
||||||
|
|
||||||
|
// check if only one NAL (fix ffmpeg transcoding for Reolink RLC-510A)
|
||||||
|
if i == len(payload) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = payload[i:]
|
||||||
|
|
||||||
|
if NALUType(payload) == NALUTypeIFrame {
|
||||||
|
buf = append(buf, ps...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect all NALs for Access Unit
|
||||||
if !packet.Marker {
|
if !packet.Marker {
|
||||||
buffer = append(buffer, payload...)
|
buf = append(buf, payload...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if buffer != nil {
|
if len(buf) > 0 {
|
||||||
payload = append(buffer, payload...)
|
payload = append(buf, payload...)
|
||||||
buffer = nil
|
buf = buf[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Printf("[AVC] %v, len: %d\n", Types(payload), len(payload))
|
//fmt.Printf(
|
||||||
|
// "[AVC] %v, len: %d, %v\n", Types(payload), len(payload),
|
||||||
switch NALUType(payload) {
|
// reflect.ValueOf(buf).Pointer() == reflect.ValueOf(payload).Pointer(),
|
||||||
case NALUTypeIFrame:
|
//)
|
||||||
payload = Join(ps, payload)
|
|
||||||
case NALUTypeSEI:
|
|
||||||
// ffmpeg with transcoding
|
|
||||||
i := 4 + binary.BigEndian.Uint32(payload)
|
|
||||||
payload = payload[i:]
|
|
||||||
if NALUType(payload) == NALUTypeIFrame {
|
|
||||||
payload = Join(ps, payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clone := *packet
|
clone := *packet
|
||||||
clone.Version = RTPPacketVersionAVC
|
clone.Version = RTPPacketVersionAVC
|
||||||
|
|||||||
@@ -61,8 +61,16 @@ func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *strea
|
|||||||
lqt, cqt = MakeTables(q)
|
lqt, cqt = MakeTables(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc2435#section-3.1.5
|
||||||
|
// The maximum width is 2040 pixels.
|
||||||
w := uint16(packet.Payload[6]) << 3
|
w := uint16(packet.Payload[6]) << 3
|
||||||
h := uint16(packet.Payload[7]) << 3
|
h := uint16(packet.Payload[7]) << 3
|
||||||
|
|
||||||
|
// fix 2560x1920 and 2560x1440
|
||||||
|
if w == 512 && (h == 1920 || h == 1440) {
|
||||||
|
w = 2560
|
||||||
|
}
|
||||||
|
|
||||||
//fmt.Printf("t: %d, q: %d, w: %d, h: %d\n", t, q, w, h)
|
//fmt.Printf("t: %d, q: %d, w: %d, h: %d\n", t, q, w, h)
|
||||||
header = MakeHeaders(t, w, h, lqt, cqt)
|
header = MakeHeaders(t, w, h, lqt, cqt)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
'<a href="api/stream.mp4?src={name}">mp4</a>',
|
'<a href="api/stream.mp4?src={name}">mp4</a>',
|
||||||
'<a href="api/frame.mp4?src={name}">frame</a>',
|
'<a href="api/frame.mp4?src={name}">frame</a>',
|
||||||
`<a href="rtsp://${location.hostname}:8554/{name}">rtsp</a>`,
|
`<a href="rtsp://${location.hostname}:8554/{name}">rtsp</a>`,
|
||||||
|
'<a href="api/stream.mjpeg?src={name}">mjpeg</a>',
|
||||||
'<a href="api/streams?src={name}">info</a>',
|
'<a href="api/streams?src={name}">info</a>',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user