mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add master m3u8 creation
This commit is contained in:
parent
b312403201
commit
4193b6b391
@ -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
|
||||
}
|
||||
|
@ -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 .
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user