Transcoder: Audio: Add quality to AudioKey + param to api endpoint

This commit is contained in:
Arthur Jamet 2026-03-11 19:32:37 +00:00 committed by Zoe Roux
parent 42ba285948
commit 7f800f82d0
4 changed files with 58 additions and 42 deletions

View File

@ -20,9 +20,9 @@ func RegisterStreamHandlers(e *echo.Group, transcoder *src.Transcoder) {
e.GET("/:path/direct/:identifier", DirectStream)
e.GET("/:path/master.m3u8", h.GetMaster)
e.GET("/:path/:video/:quality/index.m3u8", h.GetVideoIndex)
e.GET("/:path/audio/:audio/index.m3u8", h.GetAudioIndex)
e.GET("/:path/audio/:audio/:quality/index.m3u8", h.GetAudioIndex)
e.GET("/:path/:video/:quality/:chunk", h.GetVideoSegment)
e.GET("/:path/audio/:audio/:chunk", h.GetAudioSegment)
e.GET("/:path/audio/:audio/:quality/:chunk", h.GetAudioSegment)
}
// @Summary Direct video
@ -115,7 +115,7 @@ func (h *shandler) GetVideoIndex(c echo.Context) error {
// This route can take a few seconds to respond since it will way for at least one segment to be
// available.
//
// Path: /:path/audio/:audio/index.m3u8
// Path: /:path/audio/:audio/:quality/index.m3u8
//
// PRIVATE ROUTE (not documented in swagger, can change at any time)
// Only reached via the master.m3u8.
@ -124,6 +124,10 @@ func (h *shandler) GetAudioIndex(c echo.Context) error {
if err != nil {
return err
}
quality, err := src.AudioQualityFromString(c.Param("quality"))
if err != nil {
return err
}
client, err := getClientId(c)
if err != nil {
return err
@ -133,7 +137,7 @@ func (h *shandler) GetAudioIndex(c echo.Context) error {
return err
}
ret, err := h.transcoder.GetAudioIndex(c.Request().Context(), path, uint32(audio), client, sha)
ret, err := h.transcoder.GetAudioIndex(c.Request().Context(), path, uint32(audio), quality, client, sha)
if err != nil {
return err
}
@ -190,7 +194,7 @@ func (h *shandler) GetVideoSegment(c echo.Context) error {
//
// Retrieve a chunk of a transcoded audio.
//
// Path: /:path/audio/:audio/segments-:chunk.ts
// Path: /:path/audio/:audio/:quality/segments-:chunk.ts
//
// PRIVATE ROUTE (not documented in swagger, can change at any time)
// Only reached via the master.m3u8.
@ -199,6 +203,10 @@ func (h *shandler) GetAudioSegment(c echo.Context) error {
if err != nil {
return err
}
quality, err := src.AudioQualityFromString(c.Param("quality"))
if err != nil {
return err
}
segment, err := parseSegment(c.Param("chunk"))
if err != nil {
return err
@ -212,7 +220,7 @@ func (h *shandler) GetAudioSegment(c echo.Context) error {
return err
}
ret, err := h.transcoder.GetAudioSegment(c.Request().Context(), path, uint32(audio), segment, client, sha)
ret, err := h.transcoder.GetAudioSegment(c.Request().Context(), path, uint32(audio), quality, segment, client, sha)
if err != nil {
return err
}

View File

@ -19,7 +19,12 @@ type FileStream struct {
Out string
Info *MediaInfo
videos CMap[VideoKey, *VideoStream]
audios CMap[uint32, *AudioStream]
audios CMap[AudioKey, *AudioStream]
}
type AudioKey struct {
idx uint32
quality AudioQuality
}
type VideoKey struct {
@ -32,7 +37,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[uint32, *AudioStream](),
audios: NewCMap[AudioKey, *AudioStream](),
}
ret.ready.Add(1)
@ -71,27 +76,28 @@ func (fs *FileStream) Destroy() {
func (fs *FileStream) GetMaster(client string) string {
master := "#EXTM3U\n"
// TODO: support multiples audio qualities (and original)
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)
for _, quality := range AudioQualities {
for _, audio := range fs.Info.Audios {
master += "#EXT-X-MEDIA:TYPE=AUDIO,"
master += fmt.Sprintf("GROUP-ID=\"audio-%s\",", quality)
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)
}
if audio.IsDefault {
master += "DEFAULT=YES,"
}
master += "CHANNELS=\"2\","
master += fmt.Sprintf("URI=\"audio/%d/%s/index.m3u8?clientId=%s\"\n", audio.Index, quality, client)
}
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)
}
if audio.IsDefault {
master += "DEFAULT=YES,"
}
master += "CHANNELS=\"2\","
master += fmt.Sprintf("URI=\"audio/%d/index.m3u8?clientId=%s\"\n", audio.Index, client)
master += "\n"
}
master += "\n"
// codec is the prefix + the level, the level is not part of the codec we want to compare for the same_codec check bellow
transcode_prefix := "avc1.6400"
@ -204,25 +210,25 @@ func (fs *FileStream) GetVideoSegment(idx uint32, quality VideoQuality, segment
return stream.GetSegment(segment)
}
func (fs *FileStream) getAudioStream(audio uint32) (*AudioStream, error) {
stream, _ := fs.audios.GetOrCreate(audio, func() *AudioStream {
ret, _ := fs.transcoder.NewAudioStream(fs, audio)
func (fs *FileStream) getAudioStream(idx uint32, quality AudioQuality) (*AudioStream, error) {
stream, _ := fs.audios.GetOrCreate(AudioKey{idx, quality}, func() *AudioStream {
ret, _ := fs.transcoder.NewAudioStream(fs, idx, quality)
return ret
})
stream.ready.Wait()
return stream, nil
}
func (fs *FileStream) GetAudioIndex(audio uint32, client string) (string, error) {
stream, err := fs.getAudioStream(audio)
func (fs *FileStream) GetAudioIndex(idx uint32, quality AudioQuality, client string) (string, error) {
stream, err := fs.getAudioStream(idx, quality)
if err != nil {
return "", nil
}
return stream.GetIndex(client)
}
func (fs *FileStream) GetAudioSegment(audio uint32, segment int32) (string, error) {
stream, err := fs.getAudioStream(audio)
func (fs *FileStream) GetAudioSegment(idx uint32, quality AudioQuality, segment int32) (string, error) {
stream, err := fs.getAudioStream(idx, quality)
if err != nil {
return "", nil
}

View File

@ -10,7 +10,7 @@ type ClientInfo struct {
sha string
path string
video *VideoKey
audio *uint32
audio *AudioKey
vhead int32
ahead int32
}
@ -151,7 +151,7 @@ func (t *Tracker) DestroyStreamIfOld(sha string) {
stream.Destroy()
}
func (t *Tracker) KillAudioIfDead(sha string, path string, audio uint32) bool {
func (t *Tracker) KillAudioIfDead(sha string, path string, audio AudioKey) bool {
for _, stream := range t.clients {
if stream.sha == sha && stream.audio != nil && *stream.audio == audio {
return false
@ -191,7 +191,7 @@ func (t *Tracker) KillVideoIfDead(sha string, path string, video VideoKey) bool
return true
}
func (t *Tracker) KillOrphanedHeads(sha string, video *VideoKey, audio *uint32) {
func (t *Tracker) KillOrphanedHeads(sha string, video *VideoKey, audio *AudioKey) {
stream, ok := t.transcoder.streams.Get(sha)
if !ok {
return

View File

@ -94,6 +94,7 @@ func (t *Transcoder) GetAudioIndex(
ctx context.Context,
path string,
audio uint32,
quality AudioQuality,
client string,
sha string,
) (string, error) {
@ -105,18 +106,18 @@ func (t *Transcoder) GetAudioIndex(
client: client,
sha: sha,
path: path,
audio: &audio,
audio: &AudioKey{audio, quality},
vhead: -1,
ahead: -1,
}
return stream.GetAudioIndex(audio, client)
return stream.GetAudioIndex(audio, quality, client)
}
func (t *Transcoder) GetVideoSegment(
ctx context.Context,
path string,
video uint32,
quality Quality,
quality VideoQuality,
segment int32,
client string,
sha string,
@ -141,6 +142,7 @@ func (t *Transcoder) GetAudioSegment(
ctx context.Context,
path string,
audio uint32,
quality AudioQuality,
segment int32,
client string,
sha string,
@ -153,9 +155,9 @@ func (t *Transcoder) GetAudioSegment(
client: client,
sha: sha,
path: path,
audio: &audio,
audio: &AudioKey{audio, quality},
ahead: segment,
vhead: -1,
}
return stream.GetAudioSegment(audio, segment)
return stream.GetAudioSegment(audio, quality, segment)
}