Skip to content

Commit

Permalink
Fix pix_fmt for publishing to RTMP servers
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexxIT committed May 25, 2024
1 parent 0bd2fcd commit 268629f
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 9 deletions.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ POST http://localhost:1984/api/streams?dst=camera1&src=ffmpeg:http://example.com
You can publish any stream to streaming services (YouTube, Telegram, etc.) via RTMP/RTMPS. Important:

- Supported codecs: H264 for video and AAC for audio
- Pixel format should be `yuv420p`, for cameras with `yuvj420p` format you SHOULD use [transcoding](#source-ffmpeg)
- AAC audio is required for YouTube, videos without audio will not work
- You don't need to enable [RTMP module](#module-rtmp) listening for this task

You can use API:
Expand All @@ -792,16 +792,19 @@ Or config file:

```yaml
publish:
# publish stream "tplink_tapo" to Telegram
tplink_tapo: rtmps://xxx-x.rtmp.t.me/s/xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxx
# publish stream "other_camera" to Telegram and YouTube
other_camera:
# publish stream "video_audio_transcode" to Telegram
video_audio_transcode:
- rtmps://xxx-x.rtmp.t.me/s/xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxx
- rtmps://xxx.rtmp.youtube.com/live2/xxxx-xxxx-xxxx-xxxx-xxxx
# publish stream "audio_transcode" to Telegram and YouTube
audio_transcode:
- rtmps://xxx-x.rtmp.t.me/s/xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxx
- rtmp://xxx.rtmp.youtube.com/live2/xxxx-xxxx-xxxx-xxxx-xxxx
streams:
# for TP-Link cameras it's important to use transcoding because of wrong pixel format
tplink_tapo: ffmpeg:rtsp://user:pass@192.168.1.123/stream1#video=h264#hardware#audio=aac
video_audio_transcode:
- ffmpeg:rtsp://user:pass@192.168.1.123/stream1#video=h264#hardware#audio=aac
audio_transcode:
- ffmpeg:rtsp://user:pass@192.168.1.123/stream1#video=copy#audio=aac
```

- **Telegram Desktop App** > Any public or private channel or group (where you admin) > Live stream > Start with... > Start streaming.
Expand Down
4 changes: 4 additions & 0 deletions pkg/bits/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,7 @@ func (r *Reader) ReadSEGolomb() int32 {
func (r *Reader) Left() []byte {
return r.buf[r.pos:]
}

func (r *Reader) Pos() (int, byte) {
return r.pos - 1, r.bits
}
2 changes: 2 additions & 0 deletions pkg/flv/muxer.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ func (m *Muxer) GetInit() []byte {
sps, pps := h264.GetParameterSet(codec.FmtpLine)
if len(sps) == 0 {
sps = []byte{0x67, 0x42, 0x00, 0x0a, 0xf8, 0x41, 0xa2}
} else {
h264.FixPixFmt(sps)
}
if len(pps) == 0 {
pps = []byte{0x68, 0xce, 0x38, 0x80}
Expand Down
135 changes: 134 additions & 1 deletion pkg/h264/sps.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package h264

import "github.com/AlexxIT/go2rtc/pkg/bits"
import (
"fmt"

"github.com/AlexxIT/go2rtc/pkg/bits"
)

// http://www.itu.int/rec/T-REC-H.264
// https://webrtc.googlesource.com/src/+/refs/heads/main/common_video/h264/sps_parser.cc
Expand Down Expand Up @@ -229,3 +233,132 @@ func (s *SPS) scaling_list(r *bits.Reader, sizeOfScalingList int) {
}
}
}

func (s *SPS) Profile() string {
switch s.profile_idc {
case 0x42:
return "Baseline"
case 0x4D:
return "Main"
case 0x58:
return "Extended"
case 0x64:
return "High"
}
return fmt.Sprintf("0x%02X", s.profile_idc)
}

func (s *SPS) PixFmt() string {
if s.bit_depth_luma_minus8 == 0 {
switch s.chroma_format_idc {
case 1:
if s.video_full_range_flag == 1 {
return "yuvj420p"
}
return "yuv420p"
case 2:
return "yuv422p"
case 3:
return "yuv444p"
}
}
return ""
}

func (s *SPS) String() string {
return fmt.Sprintf(
"%s %d.%d, %s, %dx%d",
s.Profile(), s.level_idc/10, s.level_idc%10, s.PixFmt(), s.Width(), s.Height(),
)
}

// FixPixFmt - change yuvj420p to yuv420p in SPS
// same as "-c:v copy -bsf:v h264_metadata=video_full_range_flag=0"
func FixPixFmt(sps []byte) {
r := bits.NewReader(sps)

_ = r.ReadByte()

profile := r.ReadByte()
_ = r.ReadByte()
_ = r.ReadByte()
_ = r.ReadUEGolomb()

switch profile {
case 100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135:
n := byte(8)

if r.ReadUEGolomb() == 3 {
_ = r.ReadBit()
n = 12
}

_ = r.ReadUEGolomb()
_ = r.ReadUEGolomb()
_ = r.ReadBit()

if r.ReadBit() != 0 {
for i := byte(0); i < n; i++ {
if r.ReadBit() != 0 {
return // skip
}
}
}
}

_ = r.ReadUEGolomb()

switch r.ReadUEGolomb() {
case 0:
_ = r.ReadUEGolomb()
case 1:
_ = r.ReadBit()
_ = r.ReadSEGolomb()
_ = r.ReadSEGolomb()

n := r.ReadUEGolomb()
for i := uint32(0); i < n; i++ {
_ = r.ReadSEGolomb()
}
}

_ = r.ReadUEGolomb()
_ = r.ReadBit()

_ = r.ReadUEGolomb()
_ = r.ReadUEGolomb()

if r.ReadBit() == 0 {
_ = r.ReadBit()
}

_ = r.ReadBit()

if r.ReadBit() != 0 {
_ = r.ReadUEGolomb()
_ = r.ReadUEGolomb()
_ = r.ReadUEGolomb()
_ = r.ReadUEGolomb()
}

if r.ReadBit() != 0 {
if r.ReadBit() != 0 {
if r.ReadByte() == 255 {
_ = r.ReadUint16()
_ = r.ReadUint16()
}
}

if r.ReadBit() != 0 {
_ = r.ReadBit()
}

if r.ReadBit() != 0 {
_ = r.ReadBits8(3)
if r.ReadBit() == 1 {
pos, bit := r.Pos()
sps[pos] &= ^byte(1 << bit)
}
}
}
}

0 comments on commit 268629f

Please sign in to comment.