mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add rfc6381 codec on the /info endpoint
This commit is contained in:
parent
9a9e43269d
commit
3de751c880
130
transcoder/src/codec.go
Normal file
130
transcoder/src/codec.go
Normal file
@ -0,0 +1,130 @@
|
||||
package src
|
||||
|
||||
import (
|
||||
"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 := Or(
|
||||
mi.Parameter(kind, i, "InternetMediaType"),
|
||||
mi.Parameter(kind, i, "Format"),
|
||||
)
|
||||
|
||||
log.Printf("codec: %s", codec)
|
||||
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:])
|
||||
log.Printf("%s %s %f", format, info[1], level)
|
||||
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/x-flac", "FLAC":
|
||||
ret := "fLaC"
|
||||
return &ret
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
@ -42,8 +42,10 @@ type MediaInfo struct {
|
||||
}
|
||||
|
||||
type Video struct {
|
||||
/// The codec of this stream (defined as the RFC 6381).
|
||||
/// The human readable codec name.
|
||||
Codec string `json:"codec"`
|
||||
/// The codec of this stream (defined as the RFC 6381).
|
||||
MimeCodec *string `json:"mimeCodec"`
|
||||
/// The language of this stream (as a ISO-639-2 language code)
|
||||
Language *string `json:"language"`
|
||||
/// The max quality of this video track.
|
||||
@ -63,8 +65,10 @@ type Audio struct {
|
||||
Title *string `json:"title"`
|
||||
/// The language of this stream (as a ISO-639-2 language code)
|
||||
Language *string `json:"language"`
|
||||
/// The codec of this stream.
|
||||
/// The human readable codec name.
|
||||
Codec string `json:"codec"`
|
||||
/// The codec of this stream (defined as the RFC 6381).
|
||||
MimeCodec *string `json:"mimeCodec"`
|
||||
/// Is this stream the default one of it's type?
|
||||
IsDefault bool `json:"isDefault"`
|
||||
/// Is this stream tagged as forced? (useful only for subtitles)
|
||||
@ -263,28 +267,28 @@ func getInfo(path string, route string) (*MediaInfo, error) {
|
||||
Container: OrNull(mi.Parameter(mediainfo.StreamGeneral, 0, "Format")),
|
||||
Videos: Map(make([]Video, ParseUint(mi.Parameter(mediainfo.StreamVideo, 0, "StreamCount"))), func(_ Video, i int) Video {
|
||||
return Video{
|
||||
// This codec is not in the right format (does not include bitdepth...).
|
||||
Codec: mi.Parameter(mediainfo.StreamVideo, 0, "Format"),
|
||||
Language: OrNull(mi.Parameter(mediainfo.StreamVideo, 0, "Language")),
|
||||
Quality: QualityFromHeight(ParseUint(mi.Parameter(mediainfo.StreamVideo, 0, "Height"))),
|
||||
Width: ParseUint(mi.Parameter(mediainfo.StreamVideo, 0, "Width")),
|
||||
Height: ParseUint(mi.Parameter(mediainfo.StreamVideo, 0, "Height")),
|
||||
Codec: mi.Parameter(mediainfo.StreamVideo, i, "Format"),
|
||||
MimeCodec: GetMimeCodec(mi, mediainfo.StreamVideo, i),
|
||||
Language: OrNull(mi.Parameter(mediainfo.StreamVideo, i, "Language")),
|
||||
Quality: QualityFromHeight(ParseUint(mi.Parameter(mediainfo.StreamVideo, i, "Height"))),
|
||||
Width: ParseUint(mi.Parameter(mediainfo.StreamVideo, i, "Width")),
|
||||
Height: ParseUint(mi.Parameter(mediainfo.StreamVideo, i, "Height")),
|
||||
Bitrate: ParseUint(
|
||||
Or(
|
||||
mi.Parameter(mediainfo.StreamVideo, 0, "BitRate"),
|
||||
mi.Parameter(mediainfo.StreamVideo, 0, "OverallBitRate"),
|
||||
mi.Parameter(mediainfo.StreamVideo, 0, "BitRate_Nominal"),
|
||||
mi.Parameter(mediainfo.StreamVideo, i, "BitRate"),
|
||||
mi.Parameter(mediainfo.StreamVideo, i, "OverallBitRate"),
|
||||
mi.Parameter(mediainfo.StreamVideo, i, "BitRate_Nominal"),
|
||||
),
|
||||
),
|
||||
}
|
||||
}),
|
||||
Audios: Map(make([]Audio, ParseUint(mi.Parameter(mediainfo.StreamAudio, 0, "StreamCount"))), func(_ Audio, i int) Audio {
|
||||
return Audio{
|
||||
Index: uint32(i),
|
||||
Title: OrNull(mi.Parameter(mediainfo.StreamAudio, i, "Title")),
|
||||
Language: OrNull(mi.Parameter(mediainfo.StreamAudio, i, "Language")),
|
||||
// TODO: format is invalid. Channels count missing...
|
||||
Index: uint32(i),
|
||||
Title: OrNull(mi.Parameter(mediainfo.StreamAudio, i, "Title")),
|
||||
Language: OrNull(mi.Parameter(mediainfo.StreamAudio, i, "Language")),
|
||||
Codec: mi.Parameter(mediainfo.StreamAudio, i, "Format"),
|
||||
MimeCodec: GetMimeCodec(mi, mediainfo.StreamAudio, i),
|
||||
IsDefault: mi.Parameter(mediainfo.StreamAudio, i, "Default") == "Yes",
|
||||
IsForced: mi.Parameter(mediainfo.StreamAudio, i, "Forced") == "Yes",
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user