Add master m3u8 creation

This commit is contained in:
Zoe Roux 2024-01-13 16:33:21 +01:00
parent b312403201
commit 4193b6b391
3 changed files with 115 additions and 5 deletions

View File

@ -1,6 +1,7 @@
package src
import (
"fmt"
"math"
"os/exec"
"strconv"
@ -11,19 +12,37 @@ type FileStream struct {
Path string
Keyframes []float64
CanTransmux bool
Info *MediaInfo
streams map[Quality]TranscodeStream
}
func NewFileStream(path string) (*FileStream, error) {
info_chan := make(chan struct {
info *MediaInfo
err error
})
go func() {
ret, err := GetInfo(path)
info_chan <- struct {
info *MediaInfo
err error
}{ret, err}
}()
keyframes, can_transmux, err := GetKeyframes(path)
if err != nil {
return nil, err
}
info := <-info_chan
if info.err != nil {
return nil, err
}
return &FileStream{
Path: path,
Keyframes: keyframes,
CanTransmux: can_transmux,
Info: info.info,
streams: make(map[Quality]TranscodeStream),
}, nil
}
@ -102,5 +121,45 @@ func (fs *FileStream) Destroy() {
}
func (fs *FileStream) GetMaster() string {
return ""
master := "#EXTM3U\n"
// TODO: also check if the codec is valid in a hls before putting transmux
if fs.CanTransmux {
master += "#EXT-X-STREAM-INF:"
master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", fs.Info.Video.Bitrate)
master += fmt.Sprintf("BANDWIDTH=%d,", int(float32(fs.Info.Video.Bitrate)*1.2))
master += fmt.Sprintf("RESOLUTION=%dx%d,", fs.Info.Video.Width, fs.Info.Video.Height)
master += "AUDIO=\"audio\","
master += "CLOSED-CAPTIONS=NONE\n"
master += fmt.Sprintf("./%s/index.m3u8\n", Original)
}
aspectRatio := float32(fs.Info.Video.Width) / float32(fs.Info.Video.Height)
for _, quality := range Qualities {
if quality.Height() < fs.Info.Video.Quality.Height() && quality.AverageBitrate() < fs.Info.Video.Bitrate {
master += "#EXT-X-STREAM-INF:"
master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", quality.AverageBitrate())
master += fmt.Sprintf("BANDWIDTH=%d,", quality.MaxBitrate())
master += fmt.Sprintf("RESOLUTION=%dx%d,", int(aspectRatio*float32(quality.Height())+0.5), quality.Height())
master += "CODECS=\"avc1.640028\","
master += "AUDIO=\"audio\","
master += "CLOSED-CAPTIONS=NONE\n"
master += fmt.Sprintf("./%s/index.m3u8\n", quality)
}
}
for _, audio := range fs.Info.Audios {
master += "#EXT-X-MEDIA:TYPE=AUDIO,"
master += "GROUP-ID=\"audio\","
if audio.Language != nil {
master += fmt.Sprintf("LANGUAGE=\"%s\",", *audio.Language)
}
if audio.Title != nil {
master += fmt.Sprintf("NAME=\"%s\",", *audio.Title)
} else if audio.Language != nil {
master += fmt.Sprintf("NAME=\"%s\",", *audio.Language)
} else {
master += fmt.Sprintf("NAME=\"Audio %d\",", audio.Index)
}
master += "DEFAULT=YES,"
master += fmt.Sprintf("URI=\"./audio/%d/index.m3u8\"\n", audio.Index)
}
return master
}

View File

@ -158,10 +158,10 @@ var SubtitleExtensions = map[string]string{
"vtt": "vtt",
}
func GetInfo(path string) (MediaInfo, error) {
func GetInfo(path string) (*MediaInfo, error) {
mi, err := mediainfo.Open(path)
if err != nil {
return MediaInfo{}, err
return nil, err
}
defer mi.Close()
@ -181,7 +181,7 @@ func GetInfo(path string) (MediaInfo, error) {
chapters_begin := ParseUint(mi.Parameter(mediainfo.StreamMenu, 0, "Chapters_Pos_Begin"))
chapters_end := ParseUint(mi.Parameter(mediainfo.StreamMenu, 0, "Chapters_Pos_End"))
return MediaInfo{
return &MediaInfo{
Sha: sha,
Path: path,
// Remove leading .

View File

@ -14,6 +14,57 @@ const (
Original Quality = "original"
)
var Qualities = []Quality{P240, P360, P480, P720, P1080, P1440, P4k, P8k, Original}
// I'm not entierly sure about the values for bitrates. Double checking would be nice.
func (v Quality) AverageBitrate() uint32 {
switch v {
case P240:
return 400000
case P360:
return 800000
case P480:
return 1200000
case P720:
return 2400000
case P1080:
return 4800000
case P1440:
return 9600000
case P4k:
return 16000000
case P8k:
return 28000000
case Original:
panic("Original quality must be handled specially")
}
panic("Invalid quality value")
}
func (v Quality) MaxBitrate() uint32 {
switch v {
case P240:
return 700000
case P360:
return 1400000
case P480:
return 2100000
case P720:
return 4000000
case P1080:
return 8000000
case P1440:
return 12000000
case P4k:
return 28000000
case P8k:
return 40000000
case Original:
panic("Original quality must be handled specially")
}
panic("Invalid quality value")
}
func (q Quality) Height() uint32 {
switch q {
case P240:
@ -39,7 +90,7 @@ func (q Quality) Height() uint32 {
}
func QualityFromHeight(height uint32) Quality {
qualities := []Quality{P240, P360, P480, P720, P1080, P1440, P4k, P8k, Original}
qualities := Qualities
for _, quality := range qualities {
if quality.Height() >= height {
return quality