h264.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package h264
  2. import (
  3. "encoding/base64"
  4. "encoding/binary"
  5. "encoding/hex"
  6. "fmt"
  7. "strings"
  8. "github.com/AlexxIT/go2rtc/pkg/core"
  9. )
  10. const (
  11. NALUTypePFrame = 1 // Coded slice of a non-IDR picture
  12. NALUTypeIFrame = 5 // Coded slice of an IDR picture
  13. NALUTypeSEI = 6 // Supplemental enhancement information (SEI)
  14. NALUTypeSPS = 7 // Sequence parameter set
  15. NALUTypePPS = 8 // Picture parameter set
  16. NALUTypeAUD = 9 // Access unit delimiter
  17. )
  18. func NALUType(b []byte) byte {
  19. return b[4] & 0x1F
  20. }
  21. // IsKeyframe - check if any NALU in one AU is Keyframe
  22. func IsKeyframe(b []byte) bool {
  23. for {
  24. switch NALUType(b) {
  25. case NALUTypePFrame:
  26. return false
  27. case NALUTypeIFrame:
  28. return true
  29. }
  30. size := int(binary.BigEndian.Uint32(b)) + 4
  31. if size < len(b) {
  32. b = b[size:]
  33. continue
  34. } else {
  35. return false
  36. }
  37. }
  38. }
  39. func Join(ps, iframe []byte) []byte {
  40. b := make([]byte, len(ps)+len(iframe))
  41. i := copy(b, ps)
  42. copy(b[i:], iframe)
  43. return b
  44. }
  45. // https://developers.google.com/cast/docs/media
  46. const (
  47. ProfileBaseline = 0x42
  48. ProfileMain = 0x4D
  49. ProfileHigh = 0x64
  50. CapabilityBaseline = 0xE0
  51. CapabilityMain = 0x40
  52. )
  53. // GetProfileLevelID - get profile from fmtp line
  54. // Some devices won't play video with high level, so limit max profile and max level.
  55. // And return some profile even if fmtp line is empty.
  56. func GetProfileLevelID(fmtp string) string {
  57. // avc1.640029 - H.264 high 4.1 (Chromecast 1st and 2nd Gen)
  58. profile := byte(ProfileHigh)
  59. capab := byte(0)
  60. level := byte(41)
  61. if fmtp != "" {
  62. var conf []byte
  63. // some cameras has wrong profile-level-id
  64. // https://github.com/AlexxIT/go2rtc/issues/155
  65. if s := core.Between(fmtp, "sprop-parameter-sets=", ","); s != "" {
  66. if sps, _ := base64.StdEncoding.DecodeString(s); len(sps) >= 4 {
  67. conf = sps[1:4]
  68. }
  69. } else if s = core.Between(fmtp, "profile-level-id=", ";"); s != "" {
  70. conf, _ = hex.DecodeString(s)
  71. }
  72. if len(conf) == 3 {
  73. // sanitize profile, capab and level to supported values
  74. switch conf[0] {
  75. case ProfileBaseline, ProfileMain:
  76. profile = conf[0]
  77. }
  78. switch conf[1] {
  79. case CapabilityBaseline, CapabilityMain:
  80. capab = conf[1]
  81. }
  82. switch conf[2] {
  83. case 30, 31, 40:
  84. level = conf[2]
  85. }
  86. }
  87. }
  88. return fmt.Sprintf("%02X%02X%02X", profile, capab, level)
  89. }
  90. func GetParameterSet(fmtp string) (sps, pps []byte) {
  91. if fmtp == "" {
  92. return
  93. }
  94. s := core.Between(fmtp, "sprop-parameter-sets=", ";")
  95. if s == "" {
  96. return
  97. }
  98. i := strings.IndexByte(s, ',')
  99. if i < 0 {
  100. return
  101. }
  102. sps, _ = base64.StdEncoding.DecodeString(s[:i])
  103. pps, _ = base64.StdEncoding.DecodeString(s[i+1:])
  104. return
  105. }
  106. // GetFmtpLine from SPS+PPS+IFrame in AVC format
  107. func GetFmtpLine(avc []byte) string {
  108. s := "packetization-mode=1"
  109. for {
  110. size := 4 + int(binary.BigEndian.Uint32(avc))
  111. switch NALUType(avc) {
  112. case NALUTypeSPS:
  113. s += ";profile-level-id=" + hex.EncodeToString(avc[5:8])
  114. s += ";sprop-parameter-sets=" + base64.StdEncoding.EncodeToString(avc[4:size])
  115. case NALUTypePPS:
  116. s += "," + base64.StdEncoding.EncodeToString(avc[4:size])
  117. }
  118. if size < len(avc) {
  119. avc = avc[size:]
  120. } else {
  121. return s
  122. }
  123. }
  124. }