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
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -102,7 +102,7 @@ func (h *Handler) GetAudioIndex(c echo.Context) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -138,7 +138,7 @@ func (h *Handler) GetVideoSegment(c echo.Context) error {
|
||||
|
||||
ret, err := h.transcoder.GetVideoSegment(
|
||||
path,
|
||||
int32(video),
|
||||
uint32(video),
|
||||
quality,
|
||||
segment,
|
||||
client,
|
||||
@ -173,7 +173,7 @@ func (h *Handler) GetAudioSegment(c echo.Context) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
|
||||
type AudioStream struct {
|
||||
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)
|
||||
|
||||
keyframes, err := t.metadataService.GetKeyframes(file.Info, false, idx)
|
||||
|
@ -16,11 +16,11 @@ type FileStream struct {
|
||||
Out string
|
||||
Info *MediaInfo
|
||||
videos CMap[VideoKey, *VideoStream]
|
||||
audios CMap[int32, *AudioStream]
|
||||
audios CMap[uint32, *AudioStream]
|
||||
}
|
||||
|
||||
type VideoKey struct {
|
||||
idx int32
|
||||
idx uint32
|
||||
quality Quality
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ func (t *Transcoder) newFileStream(path string, sha string) *FileStream {
|
||||
transcoder: t,
|
||||
Out: fmt.Sprintf("%s/%s", Settings.Outpath, sha),
|
||||
videos: NewCMap[VideoKey, *VideoStream](),
|
||||
audios: NewCMap[int32, *AudioStream](),
|
||||
audios: NewCMap[uint32, *AudioStream](),
|
||||
}
|
||||
|
||||
ret.ready.Add(1)
|
||||
@ -67,51 +67,8 @@ func (fs *FileStream) Destroy() {
|
||||
|
||||
func (fs *FileStream) GetMaster() string {
|
||||
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)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: support multiples audio qualities (and original)
|
||||
for _, audio := range fs.Info.Audios {
|
||||
master += "#EXT-X-MEDIA:TYPE=AUDIO,"
|
||||
master += "GROUP-ID=\"audio\","
|
||||
@ -128,12 +85,93 @@ func (fs *FileStream) GetMaster() string {
|
||||
if audio.IsDefault {
|
||||
master += "DEFAULT=YES,"
|
||||
}
|
||||
master += "CHANNELS=\"2\","
|
||||
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
|
||||
}
|
||||
|
||||
func (fs *FileStream) getVideoStream(idx int32, quality Quality) (*VideoStream, error) {
|
||||
func (fs *FileStream) getVideoStream(idx uint32, quality Quality) (*VideoStream, error) {
|
||||
var err error
|
||||
stream, _ := fs.videos.GetOrCreate(VideoKey{idx, quality}, func() *VideoStream {
|
||||
var ret *VideoStream
|
||||
@ -148,7 +186,7 @@ func (fs *FileStream) getVideoStream(idx int32, quality Quality) (*VideoStream,
|
||||
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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -156,7 +194,7 @@ func (fs *FileStream) GetVideoIndex(idx int32, quality Quality) (string, error)
|
||||
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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -164,7 +202,7 @@ func (fs *FileStream) GetVideoSegment(idx int32, quality Quality, segment int32)
|
||||
return stream.GetSegment(segment)
|
||||
}
|
||||
|
||||
func (fs *FileStream) getAudioStream(audio int32) (*AudioStream, error) {
|
||||
func (fs *FileStream) getAudioStream(audio uint32) (*AudioStream, error) {
|
||||
var err error
|
||||
stream, _ := fs.audios.GetOrCreate(audio, func() *AudioStream {
|
||||
var ret *AudioStream
|
||||
@ -179,7 +217,7 @@ func (fs *FileStream) getAudioStream(audio int32) (*AudioStream, error) {
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
func (fs *FileStream) GetAudioIndex(audio int32) (string, error) {
|
||||
func (fs *FileStream) GetAudioIndex(audio uint32) (string, error) {
|
||||
stream, err := fs.getAudioStream(audio)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
@ -187,7 +225,7 @@ func (fs *FileStream) GetAudioIndex(audio int32) (string, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
|
@ -65,8 +65,6 @@ type Video struct {
|
||||
Codec string `json:"codec"`
|
||||
/// The codec of this stream (defined as the RFC 6381).
|
||||
MimeCodec *string `json:"mimeCodec"`
|
||||
/// The max quality of this video track.
|
||||
Quality Quality `json:"quality"`
|
||||
/// The width of the video stream
|
||||
Width uint32 `json:"width"`
|
||||
/// The height of the video stream
|
||||
@ -251,12 +249,11 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
|
||||
MimeCodec: GetMimeCodec(stream),
|
||||
Title: OrNull(stream.Tags.Title),
|
||||
Language: NullIfUnd(lang.String()),
|
||||
Quality: QualityFromHeight(uint32(stream.Height)),
|
||||
Width: uint32(stream.Width),
|
||||
Height: uint32(stream.Height),
|
||||
// ffmpeg does not report bitrate in mkv files, fallback to bitrate of the whole container
|
||||
// (bigger than the result since it contains audio and other videos but better than nothing).
|
||||
Bitrate: ParseUint(cmp.Or(stream.BitRate, mi.Format.BitRate)),
|
||||
Bitrate: ParseUint(cmp.Or(stream.BitRate, mi.Format.BitRate)),
|
||||
IsDefault: stream.Disposition.Default != 0,
|
||||
}
|
||||
}),
|
||||
@ -268,7 +265,7 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
|
||||
Language: NullIfUnd(lang.String()),
|
||||
Codec: stream.CodecName,
|
||||
MimeCodec: GetMimeCodec(stream),
|
||||
Bitrate: ParseUint(cmp.Or(stream.BitRate, mi.Format.BitRate)),
|
||||
Bitrate: ParseUint(cmp.Or(stream.BitRate, mi.Format.BitRate)),
|
||||
IsDefault: stream.Disposition.Default != 0,
|
||||
}
|
||||
}),
|
||||
@ -325,9 +322,5 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
|
||||
ret.MimeCodec = &container
|
||||
}
|
||||
}
|
||||
|
||||
if len(ret.Videos) > 0 {
|
||||
ret.Video = &ret.Videos[0]
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
@ -83,10 +83,10 @@ func (kf *Keyframe) Scan(src interface{}) error {
|
||||
type KeyframeKey struct {
|
||||
Sha string
|
||||
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{
|
||||
Sha: info.Sha,
|
||||
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.
|
||||
// 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)
|
||||
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)()
|
||||
// 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
|
||||
@ -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
|
||||
func getAudioKeyframes(info *MediaInfo, audio_idx int32, kf *Keyframe) error {
|
||||
func getAudioKeyframes(info *MediaInfo, audio_idx uint32, kf *Keyframe) error {
|
||||
dummyKeyframeDuration := float64(4)
|
||||
segmentCount := int((float64(info.Duration) / dummyKeyframeDuration) + 1)
|
||||
kf.Keyframes = make([]float64, segmentCount)
|
||||
|
@ -130,7 +130,6 @@ func (s *MetadataService) getMetadata(path string, sha string) (*MediaInfo, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.Quality = QualityFromHeight(v.Height)
|
||||
ret.Videos = append(ret.Videos, v)
|
||||
}
|
||||
|
||||
|
@ -110,10 +110,10 @@ func (q Quality) Height() uint32 {
|
||||
panic("Invalid quality value")
|
||||
}
|
||||
|
||||
func QualityFromHeight(height uint32) Quality {
|
||||
func (video *Video) Quality() Quality {
|
||||
qualities := Qualities
|
||||
for _, quality := range qualities {
|
||||
if quality.Height() >= height {
|
||||
if quality.Height() >= video.Height || quality.AverageBitrate() >= video.Bitrate {
|
||||
return quality
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ type ClientInfo struct {
|
||||
client string
|
||||
path string
|
||||
video *VideoKey
|
||||
audio int32
|
||||
audio *uint32
|
||||
vhead int32
|
||||
ahead int32
|
||||
}
|
||||
@ -60,7 +60,7 @@ func (t *Tracker) start() {
|
||||
if info.video == nil {
|
||||
info.video = old.video
|
||||
}
|
||||
if info.audio == -1 {
|
||||
if info.audio == nil {
|
||||
info.audio = old.audio
|
||||
}
|
||||
if info.vhead == -1 {
|
||||
@ -77,14 +77,14 @@ func (t *Tracker) start() {
|
||||
|
||||
// now that the new info is stored and fixed, kill old streams
|
||||
if ok && old.path == info.path {
|
||||
if old.audio != info.audio && old.audio != -1 {
|
||||
t.KillAudioIfDead(old.path, old.audio)
|
||||
if old.audio != info.audio && old.audio != nil {
|
||||
t.KillAudioIfDead(old.path, *old.audio)
|
||||
}
|
||||
if old.video != info.video && old.video != nil {
|
||||
t.KillVideoIfDead(old.path, *old.video)
|
||||
}
|
||||
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 {
|
||||
t.KillOrphanedHeads(old.path, nil, old.audio)
|
||||
@ -106,7 +106,7 @@ func (t *Tracker) start() {
|
||||
delete(t.visitDate, client)
|
||||
|
||||
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)
|
||||
if !audio_cleanup || !video_cleanup {
|
||||
t.KillOrphanedHeads(info.path, info.video, info.audio)
|
||||
@ -150,9 +150,9 @@ func (t *Tracker) DestroyStreamIfOld(path string) {
|
||||
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 {
|
||||
if stream.path == path && stream.audio == audio {
|
||||
if stream.path == path && stream.audio != nil && *stream.audio == audio {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -190,7 +190,7 @@ func (t *Tracker) KillVideoIfDead(path string, video VideoKey) bool {
|
||||
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)
|
||||
if !ok {
|
||||
return
|
||||
@ -202,8 +202,8 @@ func (t *Tracker) KillOrphanedHeads(path string, video *VideoKey, audio int32) {
|
||||
t.killOrphanedeheads(&vstream.Stream, true)
|
||||
}
|
||||
}
|
||||
if audio != -1 {
|
||||
astream, aok := stream.audios.Get(audio)
|
||||
if audio != nil {
|
||||
astream, aok := stream.audios.Get(*audio)
|
||||
if aok {
|
||||
t.killOrphanedeheads(&astream.Stream, false)
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func (t *Transcoder) GetMaster(path string, client string, sha string) (string,
|
||||
client: client,
|
||||
path: path,
|
||||
video: nil,
|
||||
audio: -1,
|
||||
audio: nil,
|
||||
vhead: -1,
|
||||
ahead: -1,
|
||||
}
|
||||
@ -65,7 +65,7 @@ func (t *Transcoder) GetMaster(path string, client string, sha string) (string,
|
||||
|
||||
func (t *Transcoder) GetVideoIndex(
|
||||
path string,
|
||||
video int32,
|
||||
video uint32,
|
||||
quality Quality,
|
||||
client string,
|
||||
sha string,
|
||||
@ -78,7 +78,7 @@ func (t *Transcoder) GetVideoIndex(
|
||||
client: client,
|
||||
path: path,
|
||||
video: &VideoKey{video, quality},
|
||||
audio: -1,
|
||||
audio: nil,
|
||||
vhead: -1,
|
||||
ahead: -1,
|
||||
}
|
||||
@ -87,7 +87,7 @@ func (t *Transcoder) GetVideoIndex(
|
||||
|
||||
func (t *Transcoder) GetAudioIndex(
|
||||
path string,
|
||||
audio int32,
|
||||
audio uint32,
|
||||
client string,
|
||||
sha string,
|
||||
) (string, error) {
|
||||
@ -98,7 +98,7 @@ func (t *Transcoder) GetAudioIndex(
|
||||
t.clientChan <- ClientInfo{
|
||||
client: client,
|
||||
path: path,
|
||||
audio: audio,
|
||||
audio: &audio,
|
||||
vhead: -1,
|
||||
ahead: -1,
|
||||
}
|
||||
@ -107,7 +107,7 @@ func (t *Transcoder) GetAudioIndex(
|
||||
|
||||
func (t *Transcoder) GetVideoSegment(
|
||||
path string,
|
||||
video int32,
|
||||
video uint32,
|
||||
quality Quality,
|
||||
segment int32,
|
||||
client string,
|
||||
@ -122,7 +122,7 @@ func (t *Transcoder) GetVideoSegment(
|
||||
path: path,
|
||||
video: &VideoKey{video, quality},
|
||||
vhead: segment,
|
||||
audio: -1,
|
||||
audio: nil,
|
||||
ahead: -1,
|
||||
}
|
||||
return stream.GetVideoSegment(video, quality, segment)
|
||||
@ -130,7 +130,7 @@ func (t *Transcoder) GetVideoSegment(
|
||||
|
||||
func (t *Transcoder) GetAudioSegment(
|
||||
path string,
|
||||
audio int32,
|
||||
audio uint32,
|
||||
segment int32,
|
||||
client string,
|
||||
sha string,
|
||||
@ -142,7 +142,7 @@ func (t *Transcoder) GetAudioSegment(
|
||||
t.clientChan <- ClientInfo{
|
||||
client: client,
|
||||
path: path,
|
||||
audio: audio,
|
||||
audio: &audio,
|
||||
ahead: segment,
|
||||
vhead: -1,
|
||||
}
|
||||
|
@ -15,3 +15,13 @@ func printExecTime(message string, args ...any) func() {
|
||||
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 {
|
||||
Stream
|
||||
idx int32
|
||||
video *Video
|
||||
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(
|
||||
"Creating a new video stream for %s (n %d) in quality %s",
|
||||
file.Info.Path,
|
||||
@ -25,8 +25,14 @@ func (t *Transcoder) NewVideoStream(file *FileStream, idx int32, quality Quality
|
||||
}
|
||||
|
||||
ret := new(VideoStream)
|
||||
ret.idx = idx
|
||||
ret.quality = quality
|
||||
for _, video := range file.Info.Videos {
|
||||
if video.Index == idx {
|
||||
ret.video = &video
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
NewStream(file, keyframes, ret, &ret.Stream)
|
||||
return ret, nil
|
||||
}
|
||||
@ -54,7 +60,7 @@ func closestMultiple(n int32, x int32) int32 {
|
||||
|
||||
func (vs *VideoStream) getTranscodeArgs(segments string) []string {
|
||||
args := []string{
|
||||
"-map", fmt.Sprint("0:V:%d", vs.idx),
|
||||
"-map", fmt.Sprint("0:V:%d", vs.video.Index),
|
||||
}
|
||||
|
||||
if vs.quality == Original {
|
||||
@ -65,7 +71,7 @@ func (vs *VideoStream) getTranscodeArgs(segments string) []string {
|
||||
}
|
||||
|
||||
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.
|
||||
width = closestMultiple(width, 2)
|
||||
args = append(args,
|
||||
|
Loading…
x
Reference in New Issue
Block a user