Kyoo/transcoder/src/codec.go
2024-05-12 17:47:21 +02:00

135 lines
2.9 KiB
Go

package src
import (
"cmp"
"fmt"
"log"
"strings"
"github.com/zoriya/go-mediainfo"
)
// convert mediainfo to RFC 6381, waiting for either of those tickets to be resolved:
//
// https://sourceforge.net/p/mediainfo/feature-requests/499
// https://trac.ffmpeg.org/ticket/6617
//
// this code is addapted from https://github.com/jellyfin/jellyfin/blob/master/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
func GetMimeCodec(mi *mediainfo.File, kind mediainfo.StreamKind, i int) *string {
codec := cmp.Or(
mi.Parameter(kind, i, "InternetMediaType"),
mi.Parameter(kind, i, "Format"),
)
switch codec {
case "video/H264", "AVC":
ret := "avc1"
info := strings.Split(strings.ToLower(mi.Parameter(kind, i, "Format_Profile")), "@")
format := info[0]
switch format {
case "high":
ret += ".6400"
case "main":
ret += ".4D40"
case "baseline":
ret += ".42E0"
default:
// Default to constrained baseline if profile is invalid
ret += ".4240"
}
// level format is l3.1 for level 31
level := ParseFloat(info[1][1:])
ret += fmt.Sprintf("%02x", int(level*10))
return &ret
case "video/H265", "HEVC":
// The h265 syntax is a bit of a mystery at the time this comment was written.
// This is what I've found through various sources:
// FORMAT: [codecTag].[profile].[constraint?].L[level * 30].[UNKNOWN]
ret := "hvc1"
info := strings.Split(strings.ToLower(mi.Parameter(kind, i, "Format_Profile")), "@")
profile := info[0]
if profile == "main 10" {
ret += ".2.4"
} else {
ret += ".1.4"
}
level := ParseFloat(info[1][:1])
ret += fmt.Sprintf(".L%02X.BO", int(level*30))
return &ret
case "AV1":
// https://aomedia.org/av1/specification/annex-a/
// FORMAT: [codecTag].[profile].[level][tier].[bitDepth]
ret := "av01"
info := strings.Split(strings.ToLower(mi.Parameter(kind, i, "Format_Profile")), "@")
profile := info[0]
switch profile {
case "main":
ret += ".0"
case "high":
ret += ".1"
case "professional":
ret += ".2"
default:
// Default to Main
ret += ".0"
}
// level is not defined in mediainfo. using a default
// Default to the maximum defined level 6.3
level := 19
bitdepth := ParseUint(mi.Parameter(kind, i, "BitDepth"))
if bitdepth != 8 && bitdepth != 10 && bitdepth != 12 {
// Default to 8 bits
bitdepth = 8
}
tierflag := 'M'
ret += fmt.Sprintf(".%02X%c.%02d", level, tierflag, bitdepth)
return &ret
case "AAC":
ret := "mp4a"
profile := strings.ToLower(mi.Parameter(kind, i, "Format_AdditionalFeatures"))
switch profile {
case "he":
ret += ".40.5"
case "lc":
ret += ".40.2"
default:
ret += ".40.2"
}
return &ret
case "audio/opus", "Opus":
ret := "Opus"
return &ret
case "AC-3":
ret := "mp4a.a5"
return &ret
case "audio/eac3", "E-AC-3":
ret := "mp4a.a6"
return &ret
case "audio/x-flac", "FLAC":
ret := "fLaC"
return &ret
default:
log.Printf("No known mime format for: %s", codec)
return nil
}
}