diff --git a/front/public/translations/en.json b/front/public/translations/en.json index 78e33aa9..6fe0a950 100644 --- a/front/public/translations/en.json +++ b/front/public/translations/en.json @@ -311,6 +311,7 @@ "video": "Video", "audio": "Audio", "subtitles": "Subtitles", + "channels": "{{channels}} Channels", "forced": "Forced", "hearing-impaired": "CC", "default": "Default", diff --git a/front/src/models/video-info.ts b/front/src/models/video-info.ts index 506599bf..80e0466d 100644 --- a/front/src/models/video-info.ts +++ b/front/src/models/video-info.ts @@ -35,6 +35,7 @@ export const AudioTrack = z.object({ language: z.string().nullable(), codec: z.string(), mimeCodec: z.string().nullable(), + channels: z.int(), bitrate: z.number(), isDefault: z.boolean(), }); diff --git a/front/src/ui/info/index.tsx b/front/src/ui/info/index.tsx index 8bd7377a..a78c6a2f 100644 --- a/front/src/ui/info/index.tsx +++ b/front/src/ui/info/index.tsx @@ -94,6 +94,7 @@ export const Info = () => { ? t("mediainfo.default") : undefined, x.codec, + t("mediainfo.channels", { channels: x.channels }), ] .filter((x) => x) .join(" - ")} diff --git a/transcoder/migrations/000003_add_audio_channels.up.sql b/transcoder/migrations/000003_add_audio_channels.up.sql index b32f6c83..f0d5140f 100644 --- a/transcoder/migrations/000003_add_audio_channels.up.sql +++ b/transcoder/migrations/000003_add_audio_channels.up.sql @@ -1,5 +1,6 @@ begin; +delete from gocoder.audios; alter table gocoder.audios add column channels int not null default 2; commit; diff --git a/transcoder/src/filestream.go b/transcoder/src/filestream.go index 9f9637fb..2efd7830 100644 --- a/transcoder/src/filestream.go +++ b/transcoder/src/filestream.go @@ -77,52 +77,12 @@ func (fs *FileStream) Destroy() { func (fs *FileStream) GetMaster(client string) string { master := "#EXTM3U\n" - for _, audio := range fs.Info.Audios { - for _, quality := range AudioQualities { - 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) - } - master += "#EXT-X-MEDIA:TYPE=AUDIO," - master += fmt.Sprintf("GROUP-ID=\"audio-%s\",", AOriginal) - 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\"," // TODO - master += fmt.Sprintf("URI=\"audio/%d/%s/index.m3u8?clientId=%s\"\n", audio.Index, AOriginal, client) - } - // 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" transcode_codec := transcode_prefix + "28" transcode_audio_codec := "mp4a.40.2" var def_video *Video - var def_audio *Audio for _, video := range fs.Info.Videos { if video.IsDefault { def_video = &video @@ -132,10 +92,53 @@ func (fs *FileStream) GetMaster(client string) string { if def_video == nil && len(fs.Info.Videos) > 0 { def_video = &fs.Info.Videos[0] } - if len(fs.Info.Audios) > 0 { + + var def_audio *Audio + for _, audio := range fs.Info.Audios { + if audio.IsDefault { + def_audio = &audio + break + } + } + if def_audio == nil && len(fs.Info.Audios) > 0 { def_audio = &fs.Info.Audios[0] } + if def_audio != nil { + aqualities := utils.Filter(AudioQualities, func(quality AudioQuality) bool { + return quality.Bitrate() < def_audio.Bitrate + }) + aqualities = append(aqualities, AOriginal) + + for _, audio := range fs.Info.Audios { + for _, quality := range slices.Backward(aqualities) { + master += "#EXT-X-MEDIA:TYPE=AUDIO," + master += fmt.Sprintf("GROUP-ID=\"a-%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 == *def_audio { + master += "DEFAULT=YES," + } + if quality == AOriginal { + master += fmt.Sprintf("CHANNELS=\"%d\",", audio.Channels) + } else { + master += "CHANNELS=\"2\"," + } + master += fmt.Sprintf("URI=\"audio/%d/%s/index.m3u8?clientId=%s\"\n", audio.Index, quality, client) + } + master += "\n" + } + master += "\n" + } + if def_video != nil { qualities := utils.Filter(VideoQualities, func(quality VideoQuality) bool { return quality.Height() < def_video.Height @@ -148,8 +151,8 @@ func (fs *FileStream) GetMaster(client string) string { } qualities = append(qualities, Original) - for _, quality := range qualities { - for _, video := range fs.Info.Videos { + for _, video := range fs.Info.Videos { + for _, quality := range slices.Backward(qualities) { master += "#EXT-X-MEDIA:TYPE=VIDEO," master += fmt.Sprintf("GROUP-ID=\"%s\",", quality) if video.Language != nil { @@ -168,6 +171,7 @@ func (fs *FileStream) GetMaster(client string) string { master += fmt.Sprintf("URI=\"%d/%s/index.m3u8?clientId=%s\"\n", video.Index, quality, client) } } + master += "\n" } master += "\n" @@ -178,23 +182,35 @@ func (fs *FileStream) GetMaster(client string) string { if def_audio != nil && (def_audio.MimeCodec == nil || *def_audio.MimeCodec != transcode_audio_codec) { audios = append(audios, matchAudioQuality(def_video.Quality())) } - for _, audio_quality := range audios { + for _, aquality := range audios { // original & noresize streams 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) - var audio_codec = transcode_audio_codec - if def_audio != nil && audio_quality == AOriginal { - audio_codec = *def_audio.MimeCodec + + codecs := make([]string, 0) + if quality == Original { + if def_video.MimeCodec != nil { + codecs = append(codecs, *def_video.MimeCodec) + } + } else { + codecs = append(codecs, transcode_codec) } - if quality != Original { - master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transcode_codec, audio_codec}, ",")) - } else if def_video.MimeCodec != nil { - master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{*def_video.MimeCodec, audio_codec}, ",")) + if aquality == AOriginal { + if def_audio != nil && def_audio.MimeCodec != nil { + codecs = append(codecs, *def_audio.MimeCodec) + } + } else { + codecs = append(codecs, transcode_audio_codec) + } + if len(codecs) > 0 { + master += fmt.Sprintf("CODECS=\"%s\",", strings.Join(codecs, ",")) + } + if def_audio != nil { + master += fmt.Sprintf("AUDIO=\"a-%s\",", string(aquality)) } - master += fmt.Sprintf("AUDIO=\"audio-%s\",", string(audio_quality)) master += "CLOSED-CAPTIONS=NONE\n" master += fmt.Sprintf("%d/%s/index.m3u8?clientId=%s\n", def_video.Index, quality, client) } @@ -206,7 +222,9 @@ func (fs *FileStream) GetMaster(client string) string { 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{transcode_codec, transcode_audio_codec}, ",")) - master += fmt.Sprintf("AUDIO=\"audio-%s\",", string(matchAudioQuality(quality))) + if def_audio != nil { + master += fmt.Sprintf("AUDIO=\"a-%s\",", string(matchAudioQuality(quality))) + } master += "CLOSED-CAPTIONS=NONE\n" master += fmt.Sprintf("%d/%s/index.m3u8?clientId=%s\n", def_video.Index, quality, client) } diff --git a/transcoder/src/metadata.go b/transcoder/src/metadata.go index ced4d9b0..4a79c912 100644 --- a/transcoder/src/metadata.go +++ b/transcoder/src/metadata.go @@ -318,7 +318,7 @@ func (s *MetadataService) storeFreshMetadata(ctx context.Context, path string, s tx.Exec( ctx, ` - insert into gocoder.audios(sha, idx, title, language, codec, mime_codec, is_default, bitrate) + insert into gocoder.audios(sha, idx, title, language, codec, mime_codec, channels, is_default, bitrate) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) on conflict (sha, idx) do update set sha = excluded.sha,