Fix ec3 playback, add channels count in media (#1420)

This commit is contained in:
Zoe Roux 2026-04-02 19:22:05 +02:00 committed by GitHub
commit 2d09b2748b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 95 additions and 59 deletions

View File

@ -311,6 +311,7 @@
"video": "Video",
"audio": "Audio",
"subtitles": "Subtitles",
"channels": "{{channels}} Channels",
"forced": "Forced",
"hearing-impaired": "CC",
"default": "Default",

View File

@ -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(),
});

View File

@ -94,6 +94,7 @@ export const Info = () => {
? t("mediainfo.default")
: undefined,
x.codec,
t("mediainfo.channels", { channels: x.channels }),
]
.filter((x) => x)
.join(" - ")}

View File

@ -0,0 +1,5 @@
begin;
alter table gocoder.audios drop column channels;
commit;

View File

@ -0,0 +1,6 @@
begin;
delete from gocoder.audios;
alter table gocoder.audios add column channels int not null default 2;
commit;

View File

@ -100,11 +100,11 @@ func GetMimeCodec(stream *ffprobe.Stream) *string {
return &ret
case "ac3":
ret := "mp4a.a5"
ret := "ac-3"
return &ret
case "eac3":
ret := "mp4a.a6"
ret := "ec-3"
return &ret
case "flac":

View File

@ -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)
}

View File

@ -17,7 +17,7 @@ import (
"gopkg.in/vansante/go-ffprobe.v2"
)
const InfoVersion = 3
const InfoVersion = 4
type Versions struct {
Info int32 `json:"info" db:"ver_info"`
@ -98,6 +98,8 @@ type Audio struct {
Codec string `json:"codec" db:"codec"`
/// The codec of this stream (defined as the RFC 6381).
MimeCodec *string `json:"mimeCodec" db:"mime_codec"`
/// The number of channels that stream has.
Channels int `json:"channels" db:"channels"`
/// The average bitrate of the audio in bytes/s
Bitrate uint32 `json:"bitrate" db:"bitrate"`
/// Is this stream the default one of it's type?
@ -280,6 +282,7 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
Language: NullIfUnd(lang.String()),
Codec: stream.CodecName,
MimeCodec: GetMimeCodec(stream),
Channels: stream.Channels,
Bitrate: ParseUint(cmp.Or(stream.BitRate, mi.Format.BitRate)),
IsDefault: stream.Disposition.Default != 0,
}

View File

@ -318,8 +318,8 @@ 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)
values ($1, $2, $3, $4, $5, $6, $7, $8)
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,
idx = excluded.idx,
@ -327,10 +327,11 @@ func (s *MetadataService) storeFreshMetadata(ctx context.Context, path string, s
language = excluded.language,
codec = excluded.codec,
mime_codec = excluded.mime_codec,
channels = excluded.channels,
is_default = excluded.is_default,
bitrate = excluded.bitrate
`,
ret.Sha, a.Index, a.Title, a.Language, a.Codec, a.MimeCodec, a.IsDefault, a.Bitrate,
ret.Sha, a.Index, a.Title, a.Language, a.Codec, a.MimeCodec, a.Channels, a.IsDefault, a.Bitrate,
)
}
for _, s := range ret.Subtitles {