mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Suport multi video files
This commit is contained in:
parent
7d3c73a1e9
commit
fa03d835ed
@ -74,7 +74,7 @@ func (h *Handler) GetVideoIndex(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := h.transcoder.GetVideoIndex(path, int32(video), quality, client, sha)
|
ret, err := h.transcoder.GetVideoIndex(path, uint32(video), quality, client, sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ func (h *Handler) GetAudioIndex(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := h.transcoder.GetAudioIndex(path, int32(audio), client, sha)
|
ret, err := h.transcoder.GetAudioIndex(path, uint32(audio), client, sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ func (h *Handler) GetVideoSegment(c echo.Context) error {
|
|||||||
|
|
||||||
ret, err := h.transcoder.GetVideoSegment(
|
ret, err := h.transcoder.GetVideoSegment(
|
||||||
path,
|
path,
|
||||||
int32(video),
|
uint32(video),
|
||||||
quality,
|
quality,
|
||||||
segment,
|
segment,
|
||||||
client,
|
client,
|
||||||
@ -173,7 +173,7 @@ func (h *Handler) GetAudioSegment(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := h.transcoder.GetAudioSegment(path, int32(audio), segment, client, sha)
|
ret, err := h.transcoder.GetAudioSegment(path, uint32(audio), segment, client, sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
type AudioStream struct {
|
type AudioStream struct {
|
||||||
Stream
|
Stream
|
||||||
index int32
|
index uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transcoder) NewAudioStream(file *FileStream, idx int32) (*AudioStream, error) {
|
func (t *Transcoder) NewAudioStream(file *FileStream, idx uint32) (*AudioStream, error) {
|
||||||
log.Printf("Creating a audio stream %d for %s", idx, file.Info.Path)
|
log.Printf("Creating a audio stream %d for %s", idx, file.Info.Path)
|
||||||
|
|
||||||
keyframes, err := t.metadataService.GetKeyframes(file.Info, false, idx)
|
keyframes, err := t.metadataService.GetKeyframes(file.Info, false, idx)
|
||||||
|
@ -16,11 +16,11 @@ type FileStream struct {
|
|||||||
Out string
|
Out string
|
||||||
Info *MediaInfo
|
Info *MediaInfo
|
||||||
videos CMap[VideoKey, *VideoStream]
|
videos CMap[VideoKey, *VideoStream]
|
||||||
audios CMap[int32, *AudioStream]
|
audios CMap[uint32, *AudioStream]
|
||||||
}
|
}
|
||||||
|
|
||||||
type VideoKey struct {
|
type VideoKey struct {
|
||||||
idx int32
|
idx uint32
|
||||||
quality Quality
|
quality Quality
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ func (t *Transcoder) newFileStream(path string, sha string) *FileStream {
|
|||||||
transcoder: t,
|
transcoder: t,
|
||||||
Out: fmt.Sprintf("%s/%s", Settings.Outpath, sha),
|
Out: fmt.Sprintf("%s/%s", Settings.Outpath, sha),
|
||||||
videos: NewCMap[VideoKey, *VideoStream](),
|
videos: NewCMap[VideoKey, *VideoStream](),
|
||||||
audios: NewCMap[int32, *AudioStream](),
|
audios: NewCMap[uint32, *AudioStream](),
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.ready.Add(1)
|
ret.ready.Add(1)
|
||||||
@ -67,51 +67,8 @@ func (fs *FileStream) Destroy() {
|
|||||||
|
|
||||||
func (fs *FileStream) GetMaster() string {
|
func (fs *FileStream) GetMaster() string {
|
||||||
master := "#EXTM3U\n"
|
master := "#EXTM3U\n"
|
||||||
if fs.Info.Video != nil {
|
|
||||||
var transmux_quality Quality
|
|
||||||
for _, quality := range Qualities {
|
|
||||||
if quality.Height() >= fs.Info.Video.Quality.Height() || quality.AverageBitrate() >= fs.Info.Video.Bitrate {
|
|
||||||
transmux_quality = quality
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// original stream
|
|
||||||
{
|
|
||||||
bitrate := float64(fs.Info.Video.Bitrate)
|
|
||||||
master += "#EXT-X-STREAM-INF:"
|
|
||||||
master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", int(math.Min(bitrate*0.8, float64(transmux_quality.AverageBitrate()))))
|
|
||||||
master += fmt.Sprintf("BANDWIDTH=%d,", int(math.Min(bitrate, float64(transmux_quality.MaxBitrate()))))
|
|
||||||
master += fmt.Sprintf("RESOLUTION=%dx%d,", fs.Info.Video.Width, fs.Info.Video.Height)
|
|
||||||
if fs.Info.Video.MimeCodec != nil {
|
|
||||||
master += fmt.Sprintf("CODECS=\"%s\",", *fs.Info.Video.MimeCodec)
|
|
||||||
}
|
|
||||||
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)
|
// TODO: support multiples audio qualities (and original)
|
||||||
// codec is the prefix + the level, the level is not part of the codec we want to compare for the same_codec check bellow
|
|
||||||
transmux_prefix := "avc1.6400"
|
|
||||||
transmux_codec := transmux_prefix + "28"
|
|
||||||
|
|
||||||
for _, quality := range Qualities {
|
|
||||||
same_codec := fs.Info.Video.MimeCodec != nil && strings.HasPrefix(*fs.Info.Video.MimeCodec, transmux_prefix)
|
|
||||||
inc_lvl := quality.Height() < fs.Info.Video.Quality.Height() ||
|
|
||||||
(quality.Height() == fs.Info.Video.Quality.Height() && !same_codec)
|
|
||||||
|
|
||||||
if inc_lvl {
|
|
||||||
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 += fmt.Sprintf("CODECS=\"%s\",", transmux_codec)
|
|
||||||
master += "AUDIO=\"audio\","
|
|
||||||
master += "CLOSED-CAPTIONS=NONE\n"
|
|
||||||
master += fmt.Sprintf("./%s/index.m3u8\n", quality)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, audio := range fs.Info.Audios {
|
for _, audio := range fs.Info.Audios {
|
||||||
master += "#EXT-X-MEDIA:TYPE=AUDIO,"
|
master += "#EXT-X-MEDIA:TYPE=AUDIO,"
|
||||||
master += "GROUP-ID=\"audio\","
|
master += "GROUP-ID=\"audio\","
|
||||||
@ -128,12 +85,93 @@ func (fs *FileStream) GetMaster() string {
|
|||||||
if audio.IsDefault {
|
if audio.IsDefault {
|
||||||
master += "DEFAULT=YES,"
|
master += "DEFAULT=YES,"
|
||||||
}
|
}
|
||||||
|
master += "CHANNELS=\"2\","
|
||||||
master += fmt.Sprintf("URI=\"./audio/%d/index.m3u8\"\n", audio.Index)
|
master += fmt.Sprintf("URI=\"./audio/%d/index.m3u8\"\n", audio.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// codec is the prefix + the level, the level is not part of the codec we want to compare for the same_codec check bellow
|
||||||
|
transmux_prefix := "avc1.6400"
|
||||||
|
transmux_codec := transmux_prefix + "28"
|
||||||
|
audio_codec := "mp4a.40.2"
|
||||||
|
|
||||||
|
var def_video *Video
|
||||||
|
for _, video := range fs.Info.Videos {
|
||||||
|
if video.IsDefault {
|
||||||
|
def_video = &video
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if def_video == nil && len(fs.Info.Videos) > 0 {
|
||||||
|
def_video = &fs.Info.Videos[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if def_video != nil {
|
||||||
|
qualities := Filter(Qualities, func(quality Quality) bool {
|
||||||
|
same_codec := def_video.MimeCodec != nil && strings.HasPrefix(*def_video.MimeCodec, transmux_prefix)
|
||||||
|
return quality.Height() < def_video.Quality().Height() ||
|
||||||
|
(quality.Height() == def_video.Quality().Height() && !same_codec)
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, quality := range qualities {
|
||||||
|
for _, video := range fs.Info.Videos {
|
||||||
|
master += "#EXT-X-MEDIA:TYPE=VIDEO,"
|
||||||
|
master += fmt.Sprintf("GROUP-ID=\"%s\",", quality)
|
||||||
|
if video.Language != nil {
|
||||||
|
master += fmt.Sprintf("LANGUAGE=\"%s\",", *video.Language)
|
||||||
|
}
|
||||||
|
if video.Title != nil {
|
||||||
|
master += fmt.Sprintf("NAME=\"%s\",", *video.Title)
|
||||||
|
} else if video.Language != nil {
|
||||||
|
master += fmt.Sprintf("NAME=\"%s\",", *video.Language)
|
||||||
|
} else {
|
||||||
|
master += fmt.Sprintf("NAME=\"Video %d\",", video.Index)
|
||||||
|
}
|
||||||
|
if &video == def_video {
|
||||||
|
master += "DEFAULT=YES"
|
||||||
|
} else {
|
||||||
|
master += fmt.Sprintf("URI=\"./%d/%s/index.m3u8\"\n", video.Index, quality)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// original stream
|
||||||
|
{
|
||||||
|
bitrate := float64(def_video.Bitrate)
|
||||||
|
master += "#EXT-X-STREAM-INF:"
|
||||||
|
master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", int(math.Min(bitrate*0.8, float64(def_video.Quality().AverageBitrate()))))
|
||||||
|
master += fmt.Sprintf("BANDWIDTH=%d,", int(math.Min(bitrate, float64(def_video.Quality().MaxBitrate()))))
|
||||||
|
master += fmt.Sprintf("RESOLUTION=%dx%d,", def_video.Width, def_video.Height)
|
||||||
|
if def_video.MimeCodec != nil {
|
||||||
|
master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{*def_video.MimeCodec, audio_codec}, ","))
|
||||||
|
}
|
||||||
|
master += "AUDIO=\"audio\","
|
||||||
|
master += "CLOSED-CAPTIONS=NONE\n"
|
||||||
|
master += fmt.Sprintf("./%d/%s/index.m3u8\n", def_video.Index, Original)
|
||||||
|
}
|
||||||
|
|
||||||
|
aspectRatio := float32(def_video.Width) / float32(def_video.Height)
|
||||||
|
|
||||||
|
for i, quality := range qualities {
|
||||||
|
if i == 0 {
|
||||||
|
// skip the original stream that already got handled
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
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 += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transmux_codec, audio_codec}, ","))
|
||||||
|
master += "AUDIO=\"audio\","
|
||||||
|
master += "CLOSED-CAPTIONS=NONE\n"
|
||||||
|
master += fmt.Sprintf("./%s/index.m3u8\n", quality)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return master
|
return master
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileStream) getVideoStream(idx int32, quality Quality) (*VideoStream, error) {
|
func (fs *FileStream) getVideoStream(idx uint32, quality Quality) (*VideoStream, error) {
|
||||||
var err error
|
var err error
|
||||||
stream, _ := fs.videos.GetOrCreate(VideoKey{idx, quality}, func() *VideoStream {
|
stream, _ := fs.videos.GetOrCreate(VideoKey{idx, quality}, func() *VideoStream {
|
||||||
var ret *VideoStream
|
var ret *VideoStream
|
||||||
@ -148,7 +186,7 @@ func (fs *FileStream) getVideoStream(idx int32, quality Quality) (*VideoStream,
|
|||||||
return stream, nil
|
return stream, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileStream) GetVideoIndex(idx int32, quality Quality) (string, error) {
|
func (fs *FileStream) GetVideoIndex(idx uint32, quality Quality) (string, error) {
|
||||||
stream, err := fs.getVideoStream(idx, quality)
|
stream, err := fs.getVideoStream(idx, quality)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -156,7 +194,7 @@ func (fs *FileStream) GetVideoIndex(idx int32, quality Quality) (string, error)
|
|||||||
return stream.GetIndex()
|
return stream.GetIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileStream) GetVideoSegment(idx int32, quality Quality, segment int32) (string, error) {
|
func (fs *FileStream) GetVideoSegment(idx uint32, quality Quality, segment int32) (string, error) {
|
||||||
stream, err := fs.getVideoStream(idx, quality)
|
stream, err := fs.getVideoStream(idx, quality)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -164,7 +202,7 @@ func (fs *FileStream) GetVideoSegment(idx int32, quality Quality, segment int32)
|
|||||||
return stream.GetSegment(segment)
|
return stream.GetSegment(segment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileStream) getAudioStream(audio int32) (*AudioStream, error) {
|
func (fs *FileStream) getAudioStream(audio uint32) (*AudioStream, error) {
|
||||||
var err error
|
var err error
|
||||||
stream, _ := fs.audios.GetOrCreate(audio, func() *AudioStream {
|
stream, _ := fs.audios.GetOrCreate(audio, func() *AudioStream {
|
||||||
var ret *AudioStream
|
var ret *AudioStream
|
||||||
@ -179,7 +217,7 @@ func (fs *FileStream) getAudioStream(audio int32) (*AudioStream, error) {
|
|||||||
return stream, nil
|
return stream, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileStream) GetAudioIndex(audio int32) (string, error) {
|
func (fs *FileStream) GetAudioIndex(audio uint32) (string, error) {
|
||||||
stream, err := fs.getAudioStream(audio)
|
stream, err := fs.getAudioStream(audio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil
|
return "", nil
|
||||||
@ -187,7 +225,7 @@ func (fs *FileStream) GetAudioIndex(audio int32) (string, error) {
|
|||||||
return stream.GetIndex()
|
return stream.GetIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileStream) GetAudioSegment(audio int32, segment int32) (string, error) {
|
func (fs *FileStream) GetAudioSegment(audio uint32, segment int32) (string, error) {
|
||||||
stream, err := fs.getAudioStream(audio)
|
stream, err := fs.getAudioStream(audio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil
|
return "", nil
|
||||||
|
@ -65,8 +65,6 @@ type Video struct {
|
|||||||
Codec string `json:"codec"`
|
Codec string `json:"codec"`
|
||||||
/// The codec of this stream (defined as the RFC 6381).
|
/// The codec of this stream (defined as the RFC 6381).
|
||||||
MimeCodec *string `json:"mimeCodec"`
|
MimeCodec *string `json:"mimeCodec"`
|
||||||
/// The max quality of this video track.
|
|
||||||
Quality Quality `json:"quality"`
|
|
||||||
/// The width of the video stream
|
/// The width of the video stream
|
||||||
Width uint32 `json:"width"`
|
Width uint32 `json:"width"`
|
||||||
/// The height of the video stream
|
/// The height of the video stream
|
||||||
@ -251,7 +249,6 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
|
|||||||
MimeCodec: GetMimeCodec(stream),
|
MimeCodec: GetMimeCodec(stream),
|
||||||
Title: OrNull(stream.Tags.Title),
|
Title: OrNull(stream.Tags.Title),
|
||||||
Language: NullIfUnd(lang.String()),
|
Language: NullIfUnd(lang.String()),
|
||||||
Quality: QualityFromHeight(uint32(stream.Height)),
|
|
||||||
Width: uint32(stream.Width),
|
Width: uint32(stream.Width),
|
||||||
Height: uint32(stream.Height),
|
Height: uint32(stream.Height),
|
||||||
// ffmpeg does not report bitrate in mkv files, fallback to bitrate of the whole container
|
// ffmpeg does not report bitrate in mkv files, fallback to bitrate of the whole container
|
||||||
@ -325,9 +322,5 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
|
|||||||
ret.MimeCodec = &container
|
ret.MimeCodec = &container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ret.Videos) > 0 {
|
|
||||||
ret.Video = &ret.Videos[0]
|
|
||||||
}
|
|
||||||
return &ret, nil
|
return &ret, nil
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,10 @@ func (kf *Keyframe) Scan(src interface{}) error {
|
|||||||
type KeyframeKey struct {
|
type KeyframeKey struct {
|
||||||
Sha string
|
Sha string
|
||||||
IsVideo bool
|
IsVideo bool
|
||||||
Index int32
|
Index uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MetadataService) GetKeyframes(info *MediaInfo, isVideo bool, idx int32) (*Keyframe, error) {
|
func (s *MetadataService) GetKeyframes(info *MediaInfo, isVideo bool, idx uint32) (*Keyframe, error) {
|
||||||
get_running, set := s.keyframeLock.Start(KeyframeKey{
|
get_running, set := s.keyframeLock.Start(KeyframeKey{
|
||||||
Sha: info.Sha,
|
Sha: info.Sha,
|
||||||
IsVideo: isVideo,
|
IsVideo: isVideo,
|
||||||
@ -138,7 +138,7 @@ func (s *MetadataService) GetKeyframes(info *MediaInfo, isVideo bool, idx int32)
|
|||||||
// Retrive video's keyframes and store them inside the kf var.
|
// Retrive video's keyframes and store them inside the kf var.
|
||||||
// Returns when all key frames are retrived (or an error occurs)
|
// Returns when all key frames are retrived (or an error occurs)
|
||||||
// info.ready.Done() is called when more than 100 are retrived (or extraction is done)
|
// info.ready.Done() is called when more than 100 are retrived (or extraction is done)
|
||||||
func getVideoKeyframes(path string, video_idx int32, kf *Keyframe) error {
|
func getVideoKeyframes(path string, video_idx uint32, kf *Keyframe) error {
|
||||||
defer printExecTime("ffprobe keyframe analysis for %s video n%d", path, video_idx)()
|
defer printExecTime("ffprobe keyframe analysis for %s video n%d", path, video_idx)()
|
||||||
// run ffprobe to return all IFrames, IFrames are points where we can split the video in segments.
|
// run ffprobe to return all IFrames, IFrames are points where we can split the video in segments.
|
||||||
// We ask ffprobe to return the time of each frame and it's flags
|
// We ask ffprobe to return the time of each frame and it's flags
|
||||||
@ -224,7 +224,7 @@ func getVideoKeyframes(path string, video_idx int32, kf *Keyframe) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we can pretty much cut audio at any point so no need to get specific frames, just cut every 4s
|
// we can pretty much cut audio at any point so no need to get specific frames, just cut every 4s
|
||||||
func getAudioKeyframes(info *MediaInfo, audio_idx int32, kf *Keyframe) error {
|
func getAudioKeyframes(info *MediaInfo, audio_idx uint32, kf *Keyframe) error {
|
||||||
dummyKeyframeDuration := float64(4)
|
dummyKeyframeDuration := float64(4)
|
||||||
segmentCount := int((float64(info.Duration) / dummyKeyframeDuration) + 1)
|
segmentCount := int((float64(info.Duration) / dummyKeyframeDuration) + 1)
|
||||||
kf.Keyframes = make([]float64, segmentCount)
|
kf.Keyframes = make([]float64, segmentCount)
|
||||||
|
@ -130,7 +130,6 @@ func (s *MetadataService) getMetadata(path string, sha string) (*MediaInfo, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
v.Quality = QualityFromHeight(v.Height)
|
|
||||||
ret.Videos = append(ret.Videos, v)
|
ret.Videos = append(ret.Videos, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +110,10 @@ func (q Quality) Height() uint32 {
|
|||||||
panic("Invalid quality value")
|
panic("Invalid quality value")
|
||||||
}
|
}
|
||||||
|
|
||||||
func QualityFromHeight(height uint32) Quality {
|
func (video *Video) Quality() Quality {
|
||||||
qualities := Qualities
|
qualities := Qualities
|
||||||
for _, quality := range qualities {
|
for _, quality := range qualities {
|
||||||
if quality.Height() >= height {
|
if quality.Height() >= video.Height || quality.AverageBitrate() >= video.Bitrate {
|
||||||
return quality
|
return quality
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ type ClientInfo struct {
|
|||||||
client string
|
client string
|
||||||
path string
|
path string
|
||||||
video *VideoKey
|
video *VideoKey
|
||||||
audio int32
|
audio *uint32
|
||||||
vhead int32
|
vhead int32
|
||||||
ahead int32
|
ahead int32
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ func (t *Tracker) start() {
|
|||||||
if info.video == nil {
|
if info.video == nil {
|
||||||
info.video = old.video
|
info.video = old.video
|
||||||
}
|
}
|
||||||
if info.audio == -1 {
|
if info.audio == nil {
|
||||||
info.audio = old.audio
|
info.audio = old.audio
|
||||||
}
|
}
|
||||||
if info.vhead == -1 {
|
if info.vhead == -1 {
|
||||||
@ -77,14 +77,14 @@ func (t *Tracker) start() {
|
|||||||
|
|
||||||
// now that the new info is stored and fixed, kill old streams
|
// now that the new info is stored and fixed, kill old streams
|
||||||
if ok && old.path == info.path {
|
if ok && old.path == info.path {
|
||||||
if old.audio != info.audio && old.audio != -1 {
|
if old.audio != info.audio && old.audio != nil {
|
||||||
t.KillAudioIfDead(old.path, old.audio)
|
t.KillAudioIfDead(old.path, *old.audio)
|
||||||
}
|
}
|
||||||
if old.video != info.video && old.video != nil {
|
if old.video != info.video && old.video != nil {
|
||||||
t.KillVideoIfDead(old.path, *old.video)
|
t.KillVideoIfDead(old.path, *old.video)
|
||||||
}
|
}
|
||||||
if old.vhead != -1 && Abs(info.vhead-old.vhead) > 100 {
|
if old.vhead != -1 && Abs(info.vhead-old.vhead) > 100 {
|
||||||
t.KillOrphanedHeads(old.path, old.video, -1)
|
t.KillOrphanedHeads(old.path, old.video, nil)
|
||||||
}
|
}
|
||||||
if old.ahead != -1 && Abs(info.ahead-old.ahead) > 100 {
|
if old.ahead != -1 && Abs(info.ahead-old.ahead) > 100 {
|
||||||
t.KillOrphanedHeads(old.path, nil, old.audio)
|
t.KillOrphanedHeads(old.path, nil, old.audio)
|
||||||
@ -106,7 +106,7 @@ func (t *Tracker) start() {
|
|||||||
delete(t.visitDate, client)
|
delete(t.visitDate, client)
|
||||||
|
|
||||||
if !t.KillStreamIfDead(info.path) {
|
if !t.KillStreamIfDead(info.path) {
|
||||||
audio_cleanup := info.audio != -1 && t.KillAudioIfDead(info.path, info.audio)
|
audio_cleanup := info.audio != nil && t.KillAudioIfDead(info.path, *info.audio)
|
||||||
video_cleanup := info.video != nil && t.KillVideoIfDead(info.path, *info.video)
|
video_cleanup := info.video != nil && t.KillVideoIfDead(info.path, *info.video)
|
||||||
if !audio_cleanup || !video_cleanup {
|
if !audio_cleanup || !video_cleanup {
|
||||||
t.KillOrphanedHeads(info.path, info.video, info.audio)
|
t.KillOrphanedHeads(info.path, info.video, info.audio)
|
||||||
@ -150,9 +150,9 @@ func (t *Tracker) DestroyStreamIfOld(path string) {
|
|||||||
stream.Destroy()
|
stream.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) KillAudioIfDead(path string, audio int32) bool {
|
func (t *Tracker) KillAudioIfDead(path string, audio uint32) bool {
|
||||||
for _, stream := range t.clients {
|
for _, stream := range t.clients {
|
||||||
if stream.path == path && stream.audio == audio {
|
if stream.path == path && stream.audio != nil && *stream.audio == audio {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ func (t *Tracker) KillVideoIfDead(path string, video VideoKey) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) KillOrphanedHeads(path string, video *VideoKey, audio int32) {
|
func (t *Tracker) KillOrphanedHeads(path string, video *VideoKey, audio *uint32) {
|
||||||
stream, ok := t.transcoder.streams.Get(path)
|
stream, ok := t.transcoder.streams.Get(path)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@ -202,8 +202,8 @@ func (t *Tracker) KillOrphanedHeads(path string, video *VideoKey, audio int32) {
|
|||||||
t.killOrphanedeheads(&vstream.Stream, true)
|
t.killOrphanedeheads(&vstream.Stream, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if audio != -1 {
|
if audio != nil {
|
||||||
astream, aok := stream.audios.Get(audio)
|
astream, aok := stream.audios.Get(*audio)
|
||||||
if aok {
|
if aok {
|
||||||
t.killOrphanedeheads(&astream.Stream, false)
|
t.killOrphanedeheads(&astream.Stream, false)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func (t *Transcoder) GetMaster(path string, client string, sha string) (string,
|
|||||||
client: client,
|
client: client,
|
||||||
path: path,
|
path: path,
|
||||||
video: nil,
|
video: nil,
|
||||||
audio: -1,
|
audio: nil,
|
||||||
vhead: -1,
|
vhead: -1,
|
||||||
ahead: -1,
|
ahead: -1,
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ func (t *Transcoder) GetMaster(path string, client string, sha string) (string,
|
|||||||
|
|
||||||
func (t *Transcoder) GetVideoIndex(
|
func (t *Transcoder) GetVideoIndex(
|
||||||
path string,
|
path string,
|
||||||
video int32,
|
video uint32,
|
||||||
quality Quality,
|
quality Quality,
|
||||||
client string,
|
client string,
|
||||||
sha string,
|
sha string,
|
||||||
@ -78,7 +78,7 @@ func (t *Transcoder) GetVideoIndex(
|
|||||||
client: client,
|
client: client,
|
||||||
path: path,
|
path: path,
|
||||||
video: &VideoKey{video, quality},
|
video: &VideoKey{video, quality},
|
||||||
audio: -1,
|
audio: nil,
|
||||||
vhead: -1,
|
vhead: -1,
|
||||||
ahead: -1,
|
ahead: -1,
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ func (t *Transcoder) GetVideoIndex(
|
|||||||
|
|
||||||
func (t *Transcoder) GetAudioIndex(
|
func (t *Transcoder) GetAudioIndex(
|
||||||
path string,
|
path string,
|
||||||
audio int32,
|
audio uint32,
|
||||||
client string,
|
client string,
|
||||||
sha string,
|
sha string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
@ -98,7 +98,7 @@ func (t *Transcoder) GetAudioIndex(
|
|||||||
t.clientChan <- ClientInfo{
|
t.clientChan <- ClientInfo{
|
||||||
client: client,
|
client: client,
|
||||||
path: path,
|
path: path,
|
||||||
audio: audio,
|
audio: &audio,
|
||||||
vhead: -1,
|
vhead: -1,
|
||||||
ahead: -1,
|
ahead: -1,
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ func (t *Transcoder) GetAudioIndex(
|
|||||||
|
|
||||||
func (t *Transcoder) GetVideoSegment(
|
func (t *Transcoder) GetVideoSegment(
|
||||||
path string,
|
path string,
|
||||||
video int32,
|
video uint32,
|
||||||
quality Quality,
|
quality Quality,
|
||||||
segment int32,
|
segment int32,
|
||||||
client string,
|
client string,
|
||||||
@ -122,7 +122,7 @@ func (t *Transcoder) GetVideoSegment(
|
|||||||
path: path,
|
path: path,
|
||||||
video: &VideoKey{video, quality},
|
video: &VideoKey{video, quality},
|
||||||
vhead: segment,
|
vhead: segment,
|
||||||
audio: -1,
|
audio: nil,
|
||||||
ahead: -1,
|
ahead: -1,
|
||||||
}
|
}
|
||||||
return stream.GetVideoSegment(video, quality, segment)
|
return stream.GetVideoSegment(video, quality, segment)
|
||||||
@ -130,7 +130,7 @@ func (t *Transcoder) GetVideoSegment(
|
|||||||
|
|
||||||
func (t *Transcoder) GetAudioSegment(
|
func (t *Transcoder) GetAudioSegment(
|
||||||
path string,
|
path string,
|
||||||
audio int32,
|
audio uint32,
|
||||||
segment int32,
|
segment int32,
|
||||||
client string,
|
client string,
|
||||||
sha string,
|
sha string,
|
||||||
@ -142,7 +142,7 @@ func (t *Transcoder) GetAudioSegment(
|
|||||||
t.clientChan <- ClientInfo{
|
t.clientChan <- ClientInfo{
|
||||||
client: client,
|
client: client,
|
||||||
path: path,
|
path: path,
|
||||||
audio: audio,
|
audio: &audio,
|
||||||
ahead: segment,
|
ahead: segment,
|
||||||
vhead: -1,
|
vhead: -1,
|
||||||
}
|
}
|
||||||
|
@ -15,3 +15,13 @@ func printExecTime(message string, args ...any) func() {
|
|||||||
log.Printf("%s finished in %s", msg, time.Since(start))
|
log.Printf("%s finished in %s", msg, time.Since(start))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Filter[E any](s []E, f func(E) bool) []E {
|
||||||
|
s2 := make([]E, 0, len(s))
|
||||||
|
for _, e := range s {
|
||||||
|
if f(e) {
|
||||||
|
s2 = append(s2, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s2
|
||||||
|
}
|
||||||
|
@ -7,11 +7,11 @@ import (
|
|||||||
|
|
||||||
type VideoStream struct {
|
type VideoStream struct {
|
||||||
Stream
|
Stream
|
||||||
idx int32
|
video *Video
|
||||||
quality Quality
|
quality Quality
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transcoder) NewVideoStream(file *FileStream, idx int32, quality Quality) (*VideoStream, error) {
|
func (t *Transcoder) NewVideoStream(file *FileStream, idx uint32, quality Quality) (*VideoStream, error) {
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"Creating a new video stream for %s (n %d) in quality %s",
|
"Creating a new video stream for %s (n %d) in quality %s",
|
||||||
file.Info.Path,
|
file.Info.Path,
|
||||||
@ -25,8 +25,14 @@ func (t *Transcoder) NewVideoStream(file *FileStream, idx int32, quality Quality
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret := new(VideoStream)
|
ret := new(VideoStream)
|
||||||
ret.idx = idx
|
|
||||||
ret.quality = quality
|
ret.quality = quality
|
||||||
|
for _, video := range file.Info.Videos {
|
||||||
|
if video.Index == idx {
|
||||||
|
ret.video = &video
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NewStream(file, keyframes, ret, &ret.Stream)
|
NewStream(file, keyframes, ret, &ret.Stream)
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
@ -54,7 +60,7 @@ func closestMultiple(n int32, x int32) int32 {
|
|||||||
|
|
||||||
func (vs *VideoStream) getTranscodeArgs(segments string) []string {
|
func (vs *VideoStream) getTranscodeArgs(segments string) []string {
|
||||||
args := []string{
|
args := []string{
|
||||||
"-map", fmt.Sprint("0:V:%d", vs.idx),
|
"-map", fmt.Sprint("0:V:%d", vs.video.Index),
|
||||||
}
|
}
|
||||||
|
|
||||||
if vs.quality == Original {
|
if vs.quality == Original {
|
||||||
@ -65,7 +71,7 @@ func (vs *VideoStream) getTranscodeArgs(segments string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
args = append(args, Settings.HwAccel.EncodeFlags...)
|
args = append(args, Settings.HwAccel.EncodeFlags...)
|
||||||
width := int32(float64(vs.quality.Height()) / float64(vs.file.Info.Video.Height) * float64(vs.file.Info.Video.Width))
|
width := int32(float64(vs.quality.Height()) / float64(vs.video.Height) * float64(vs.video.Width))
|
||||||
// force a width that is a multiple of two else some apps behave badly.
|
// force a width that is a multiple of two else some apps behave badly.
|
||||||
width = closestMultiple(width, 2)
|
width = closestMultiple(width, 2)
|
||||||
args = append(args,
|
args = append(args,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user