Live video on the AT Protocol
79
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v0.8.2 210 lines 4.6 kB view raw
1package renditions 2 3import ( 4 "fmt" 5 "math" 6 7 "stream.place/streamplace/pkg/streamplace" 8) 9 10type FPS struct { 11 Passthrough bool 12 Num uint 13 Den uint 14} 15 16type Rendition struct { 17 Width int64 18 Height int64 19 Bitrate int 20 Framerate FPS 21 Profile string 22 Name string 23 Parent *Rendition 24} 25 26type JSONProfile struct { 27 Name string `json:"name,omitempty"` 28 Width int `json:"width,omitempty"` 29 Height int `json:"height,omitempty"` 30 Bitrate int `json:"bitrate,omitempty"` 31 FPS uint `json:"fps,omitempty"` 32 FPSDen uint `json:"fpsDen,omitempty"` 33 Profile string `json:"profile,omitempty"` 34 GOP string `json:"gop,omitempty"` 35 Encoder string `json:"encoder,omitempty"` 36 Quality uint `json:"quality,omitempty"` 37} 38 39func (r Rendition) ToLivepeerProfile() JSONProfile { 40 p := JSONProfile{ 41 Name: r.Name, 42 Bitrate: r.Bitrate, 43 FPS: r.Framerate.Num, 44 FPSDen: r.Framerate.Den, 45 Profile: r.Profile, 46 } 47 if r.Parent == nil { 48 p.Width = int(r.Width) 49 p.Height = int(r.Height) 50 } else { 51 // We want to set the dimension that is the same as the parent 52 if r.Width < r.Height { 53 if r.Parent.Width == r.Height { 54 p.Height = int(r.Parent.Width) 55 } else { 56 p.Width = int(r.Parent.Height) 57 } 58 } else { 59 if r.Parent.Height == r.Height { 60 p.Height = int(r.Parent.Height) 61 } else { 62 p.Width = int(r.Parent.Width) 63 } 64 } 65 } 66 return p 67} 68 69type Renditions []Rendition 70 71func (rs Renditions) ToLivepeerProfiles() []JSONProfile { 72 profiles := make([]JSONProfile, len(rs)) 73 for i, r := range rs { 74 profiles[i] = r.ToLivepeerProfile() 75 } 76 return profiles 77} 78 79var DesiredRenditions = []Rendition{ 80 { 81 Name: "1080p", 82 Width: 1920, 83 Height: 1080, 84 Bitrate: 6_000_000, 85 Framerate: FPS{ 86 Num: 60, 87 Den: 1, 88 }, 89 Profile: "h264constrainedhigh", 90 }, 91 { 92 Name: "720p", 93 Width: 1280, 94 Height: 720, 95 Bitrate: 3_000_000, 96 Framerate: FPS{ 97 Num: 60, 98 Den: 1, 99 }, 100 Profile: "h264constrainedhigh", 101 }, 102 { 103 Name: "360p", 104 Width: 640, 105 Height: 360, 106 Bitrate: 1_000_000, 107 Framerate: FPS{ 108 Num: 30, 109 Den: 1, 110 }, 111 Profile: "h264constrainedhigh", 112 }, 113 { 114 Name: "240p", 115 Width: 426, 116 Height: 240, 117 Bitrate: 500_000, 118 Framerate: FPS{ 119 Num: 30, 120 Den: 1, 121 }, 122 Profile: "h264constrainedhigh", 123 }, 124 { 125 Name: "160p", 126 Width: 284, 127 Height: 160, 128 Bitrate: 250_000, 129 Framerate: FPS{ 130 Num: 30, 131 Den: 1, 132 }, 133 Profile: "h264baseline", 134 }, 135} 136 137// GenerateRenditions generates renditions for a given spseg 138func GenerateRenditions(spseg *streamplace.Segment) (Renditions, error) { 139 vid := spseg.Video[0] 140 if vid == nil { 141 return nil, fmt.Errorf("no video stream found") 142 } 143 rs := []Rendition{} 144 for _, r := range DesiredRenditions { 145 vidWidth := int64(vid.Width) 146 vidHeight := int64(vid.Height) 147 vertical := vid.Height > vid.Width 148 // do all the math as if it's horizontal then flip at the end 149 if vertical { 150 vidWidth, vidHeight = vidHeight, vidWidth 151 } 152 if vidWidth <= r.Width && vidHeight <= r.Height { 153 continue 154 } 155 rAspectRatio := float64(r.Width) / float64(r.Height) 156 vidAspectRatio := float64(vidWidth) / float64(vidHeight) 157 if vidAspectRatio > rAspectRatio { 158 // vid is wider than r 159 // scale down to r.Width 160 scale := float64(r.Width) / float64(vidWidth) 161 vidWidth = r.Width 162 vidHeight = int64(math.Round(float64(vidHeight) * scale)) 163 } else { 164 // vid is taller than r 165 // scale down to r.Height 166 scale := float64(r.Height) / float64(vidHeight) 167 vidHeight = r.Height 168 vidWidth = int64(math.Round(float64(vidWidth) * scale)) 169 } 170 outR := Rendition{ 171 Name: r.Name, 172 Parent: &r, 173 Profile: r.Profile, 174 } 175 if vertical { 176 outR.Width = vidHeight 177 outR.Height = vidWidth 178 } else { 179 outR.Width = vidWidth 180 outR.Height = vidHeight 181 } 182 183 // if vertical { 184 // ratio := float64(r.Height) / float64(vid.Height) 185 // outR.Height = int64(float64(vid.Width) * (16.0 / 9.0) * ratio) 186 // outR.Width = r.Height 187 // } else { 188 // ratio := float64(r.Width) / float64(vid.Width) 189 // outR.Width = r.Width 190 // outR.Height = int64(float64(vid.Width) * (9.0 / 16.0) * ratio) 191 // } 192 if vid.Framerate.Den > 0 { 193 vidFPS := float64(vid.Framerate.Num) / float64(vid.Framerate.Den) 194 rFPS := float64(r.Framerate.Num) / float64(r.Framerate.Den) 195 delta := rFPS / vidFPS 196 197 if rFPS < vidFPS { 198 if delta < 0.75 { 199 outR.Framerate.Num = uint(vid.Framerate.Num) 200 outR.Framerate.Den = uint(vid.Framerate.Den * 2) 201 } 202 } 203 } 204 205 outR.Bitrate = r.Bitrate 206 outR.Profile = r.Profile 207 rs = append(rs, outR) 208 } 209 return rs, nil 210}