Media: Refactor video content type constants #4770

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-02-05 01:23:22 +01:00
parent 6a89519e63
commit d5ef7bf1bc
15 changed files with 78 additions and 53 deletions

View File

@@ -128,6 +128,6 @@ func AbortVideo(c *gin.Context) {
func AbortVideoWithStatus(c *gin.Context, code int) {
if c != nil {
c.Data(code, header.ContentTypeMp4Avc720, brokenVideo)
c.Data(code, header.ContentTypeMp4AvcMain, brokenVideo)
}
}

View File

@@ -153,7 +153,7 @@ func GetVideo(router *gin.RouterGroup) {
if avcFile, avcErr := conv.ToAvc(mediaFile, get.Config().FFmpegEncoder(), false, false); avcFile != nil && avcErr == nil {
videoFileName = avcFile.FileName()
AddContentTypeHeader(c, header.ContentTypeMp4Avc)
AddContentTypeHeader(c, header.ContentTypeMp4AvcHigh)
} else {
// Log error and default to 404.mp4
log.Errorf("video: failed to transcode %s", clean.Log(f.FileName))

View File

@@ -15,9 +15,9 @@ import (
func TestGetVideo(t *testing.T) {
t.Run("ContentTypeAVC", func(t *testing.T) {
assert.Equal(t, header.ContentTypeMp4Avc, clean.ContentType("video/mp4; codecs=\"avc1\""))
assert.Equal(t, header.ContentTypeMp4AvcHigh, clean.ContentType("video/mp4; codecs=\"avc1\""))
mimeType := fmt.Sprintf("video/mp4; codecs=\"%s\"", clean.Codec("avc1"))
assert.Equal(t, header.ContentTypeMp4Avc, video.ContentType(mimeType, "mp4", "avc1"))
assert.Equal(t, header.ContentTypeMp4AvcHigh, video.ContentType(mimeType, "mp4", "avc1"))
})
t.Run("NoHash", func(t *testing.T) {

View File

@@ -886,7 +886,7 @@ func TestFile_ContentType(t *testing.T) {
t.Run("Video", func(t *testing.T) {
avc := FileFixtures.Get("Video.mp4")
assert.Equal(t, true, avc.FileVideo)
assert.Equal(t, header.ContentTypeMp4Avc, avc.ContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, avc.ContentType())
hevc := FileFixtures.Get("Photo21.mp4")
assert.Equal(t, true, hevc.FileVideo)
assert.Equal(t, header.ContentTypeMp4Hevc, hevc.ContentType())

View File

@@ -181,7 +181,7 @@ func TestPhoto_MediaInfo(t *testing.T) {
{
FileVideo: true,
MediaType: media.Video.String(),
FileMime: header.ContentTypeMp4Avc,
FileMime: header.ContentTypeMp4AvcHigh,
FileCodec: video.CodecAvc,
FileHash: "53c89dcfa006c9e592dd9e6db4b31cd57be64b81",
},
@@ -193,7 +193,7 @@ func TestPhoto_MediaInfo(t *testing.T) {
mediaHash, mediaCodec, mediaMime := r.MediaInfo()
assert.Equal(t, "53c89dcfa006c9e592dd9e6db4b31cd57be64b81", mediaHash)
assert.Equal(t, video.CodecAvc, mediaCodec)
assert.Equal(t, header.ContentTypeMp4Avc, mediaMime)
assert.Equal(t, header.ContentTypeMp4AvcHigh, mediaMime)
})
t.Run("VideoCodecHVC", func(t *testing.T) {
r := Photo{
@@ -216,7 +216,7 @@ func TestPhoto_MediaInfo(t *testing.T) {
{
FileVideo: true,
MediaType: media.Video.String(),
FileMime: header.ContentTypeMp4Avc,
FileMime: header.ContentTypeMp4AvcHigh,
FileCodec: "xyz",
FileHash: "",
},
@@ -231,7 +231,7 @@ func TestPhoto_MediaInfo(t *testing.T) {
FileVideo: true,
MediaType: media.Video.String(),
FileCodec: video.CodecAvc,
FileMime: header.ContentTypeMp4Avc,
FileMime: header.ContentTypeMp4AvcHigh,
FileHash: "ddb3f44eb500d7669cbe0a95e66d5a63f642487d",
},
},
@@ -259,7 +259,7 @@ func TestPhoto_MediaInfo(t *testing.T) {
{
FileVideo: true,
MediaType: media.Video.String(),
FileMime: header.ContentTypeMp4Avc,
FileMime: header.ContentTypeMp4AvcHigh,
FileHash: "",
},
},

View File

@@ -37,7 +37,7 @@ func ContentType(s string) string {
return header.ContentTypeJpeg
case "video/mp4; codecs=\"avc\"",
"video/mp4; codecs=\"avc1\"":
return header.ContentTypeMp4Avc // Advanced Video Coding (AVC), also known as H.264
return header.ContentTypeMp4AvcHigh // Advanced Video Coding (AVC), also known as H.264
case "video/mp4; codecs=\"hvc\"",
"video/mp4; codecs=\"hvc1\"",
"video/mp4; codecs=\"hevc\"":

View File

@@ -30,6 +30,6 @@ func TestContent(t *testing.T) {
assert.Equal(t, "image/png", ContentTypePng)
assert.Equal(t, "image/jpeg", ContentTypeJpeg)
assert.Equal(t, "image/svg+xml", ContentTypeSVG)
assert.Equal(t, "video/mp4; codecs=\"avc1.640028\"", ContentTypeMp4Avc)
assert.Equal(t, "video/mp4; codecs=\"avc1.640028\"", ContentTypeMp4AvcHigh)
})
}

View File

@@ -7,24 +7,36 @@ package header
- https://ott.dolby.com/codec_test/index.html
- https://dmnsgn.github.io/media-codecs/
- https://cconcolato.github.io/media-mime-support/
- https://cconcolato.github.io/media-mime-support/mediacapabilities.html
- https://thorium.rocks/misc/h265-tester.html
- https://developers.google.com/cast/docs/media
- https://privacycheck.sec.lrz.de/active/fp_cpt/fp_can_play_type.html
- https://chromium.googlesource.com/chromium/src.git/+/62.0.3178.1/content/browser/media/media_canplaytype_browsertest.cc
*/
// Standard ContentType identifiers for audio and video files.
const (
ContentTypeMov = "video/quicktime"
ContentTypeMp4 = "video/mp4"
ContentTypeMp4Avc720 = ContentTypeMp4 + "; codecs=\"avc1.640020\"" // MPEG-4 AVC, High Profile Level 3.2
ContentTypeMp4Avc = ContentTypeMp4 + "; codecs=\"avc1.640028\"" // MPEG-4 AVC, High Profile Level 4.0
ContentTypeMp4Hevc = ContentTypeMp4 + "; codecs=\"hvc1.2.4.L120.B0\"" // HEVC Mp4 Main10 Profile, Main Tier, Level 4.0
ContentTypeMp4Hev1 = ContentTypeMp4 + "; codecs=\"hev1.2.4.L120.B0\"" // HEVC Bitstream, not supported on macOS
ContentTypeMp4AvcBaseline = ContentTypeMp4 + "; codecs=\"avc1.420028\"" // MPEG-4 AVC (H.264), Baseline Level 4.0
ContentTypeMp4AvcMain = ContentTypeMp4 + "; codecs=\"avc1.4d0028\"" // MPEG-4 AVC (H.264), Main Level 4.0
ContentTypeMp4AvcHigh = ContentTypeMp4 + "; codecs=\"avc1.640028\"" // MPEG-4 AVC (H.264), High Level 4.0
ContentTypeMp4Avc3High = ContentTypeMp4 + "; codecs=\"avc3.640028\"" // MPEG-4 AVC Bitstream, High Profile, may not be supported on macOS
ContentTypeMp4Hevc = ContentTypeMp4 + "; codecs=\"hvc1.1.6.L93.B0\"" // MPEG-4 HEVC (H.265), Main Profile
ContentTypeMp4HevcHDR = ContentTypeMp4 + "; codecs=\"hev1.2.4.L153.B0\"" // MPEG-4 HEVC (H.265), Main 10 Profile
ContentTypeMp4Hev1 = ContentTypeMp4 + "; codecs=\"hev1.1.6.L93.B0\"" // MPEG-4 HEVC Bitstream, Main Profile, not supported on macOS
ContentTypeMp4Hev1HDR = ContentTypeMp4 + "; codecs=\"hev1.2.4.L153.B0\"" // MPEG-4 HEVC Bitstream, Main 10 Profile, not supported on macOS
ContentTypeMp4Vvc = ContentTypeMp4 + "; codecs=\"vvc1\"" // Versatile Video Coding (VVC), also known as H.266
ContentTypeMp4Evc = ContentTypeMp4 + "; codecs=\"evc1\"" // MPEG-5 Essential Video Coding (EVC), also known as ISO/IEC 23094-1
ContentTypeTheora = "video/ogg"
ContentTypeMov = "video/quicktime"
ContentTypeMovAvcMain = ContentTypeMov + "; codecs=\"avc1.4d0028\"" // Apple QuickTime AVC, Main Level 4.0
ContentTypeMovAvcHigh = ContentTypeMov + "; codecs=\"avc1.640028\"" // Apple QuickTime AVC, High Level 4.0
ContentTypeOgg = "video/ogg"
ContentTypeOggVorbis = ContentTypeOgg + "; codecs=\"vorbis\""
ContentTypeOggTheora = ContentTypeOgg + "; codecs=\"theora, vorbis\""
ContentTypeWebm = "video/webm"
ContentTypeWebmVp8 = "video/webm; codecs=\"vp8\""
ContentTypeWebmVp9 = "video/webm; codecs=\"vp09.00.10.08\""
ContentTypeWebmAv1 = "video/webm; codecs=\"av01.2.10M.10\""
ContentTypeWebmVp8 = ContentTypeWebm + "; codecs=\"vp8\""
ContentTypeWebmVp9 = ContentTypeWebm + "; codecs=\"vp09.00.10.08\""
ContentTypeWebmAv1 = ContentTypeWebm + "; codecs=\"av01.2.10M.10\""
)
// Standard ContentType identifiers for images and vector graphics.

View File

@@ -1,18 +1,15 @@
package video
// Profile represents a video codec profile name,
// see https://en.wikipedia.org/wiki/Advanced_Video_Coding#Profiles.
type Profile = string
const (
ProfileBaseline Profile = "BaseLine"
ProfileMain Profile = "Main"
ProfileHigh Profile = "High"
)
// Profiles contains common video codec profiles together with their ContentType ID,
// maximum bitrate, resolution, and frame rate (if known).
var Profiles = CodecProfiles{
// AvcProfiles contains common MPEG-4 AVC profiles together with their ContentType ID,
// maximum bitrate, resolution, and frame rate (if known):
// - https://dmnsgn.github.io/media-codecs/#AVC
// - https://en.wikipedia.org/wiki/Advanced_Video_Coding#Profiles
// - https://ott.dolby.com/codec_test/index.html
// - https://cconcolato.github.io/media-mime-support/
// - https://w3c.github.io/media-capabilities/
// - https://privacycheck.sec.lrz.de/active/fp_cpt/fp_can_play_type.html
// - https://chromium.googlesource.com/chromium/src.git/+/62.0.3178.1/content/browser/media/media_canplaytype_browsertest.cc
var AvcProfiles = CodecProfiles{
{Codec: CodecAvc, Profile: ProfileBaseline, Level: 30, Bitrate: 10000, ID: "avc1.66.30"}, // iOS friendly
{Codec: CodecAvc, Profile: ProfileBaseline, Level: 30, Bitrate: 10000, ID: "avc1.42001e"},
{Codec: CodecAvc, Profile: ProfileBaseline, Level: 31, Bitrate: 14000, ID: "avc1.42001f"},

View File

@@ -1,5 +1,17 @@
package video
// Profile represents a video codec profile name,
// see https://en.wikipedia.org/wiki/Advanced_Video_Coding#Profiles.
type Profile = string
const (
ProfileBaseline Profile = "Baseline"
ProfileMain Profile = "Main"
ProfileHigh Profile = "High"
)
// CodecProfile represents a codec subtype with its standardized ID,
// maximum bitrate, resolution, and frame rate (if known).
type CodecProfile struct {
Codec Codec
Profile string
@@ -11,4 +23,5 @@ type CodecProfile struct {
ID string
}
// CodecProfiles represents a set of codec subtypes.
type CodecProfiles []CodecProfile

View File

@@ -27,10 +27,12 @@ func ContentType(mediaType, fileType, videoCodec string) string {
switch c {
case "mov", "mp4":
mediaType = header.ContentTypeMp4
case CodecAvc, "avc":
mediaType = header.ContentTypeMp4Avc // Advanced Video Coding (AVC), also known as H.264
case CodecHevc, "hvc", "hevc":
mediaType = header.ContentTypeMp4Hevc // High Efficiency Video Coding (HEVC), also known as H.265
case CodecAvc3:
mediaType = header.ContentTypeMp4Avc3High // AVC (H.264) Bitstream, High Profile
case string(fs.VideoAvc), CodecAvc:
mediaType = header.ContentTypeMp4AvcHigh // Advanced Video Coding (H.264), High Profile
case string(fs.VideoHevc), CodecHevc, "hvc":
mediaType = header.ContentTypeMp4Hevc // High Efficiency Video Coding (H.265)
case CodecHev1, "hev":
mediaType = header.ContentTypeMp4Hev1 // High Efficiency Video Coding (HEVC) Bitstream
case CodecVvc, "vvc":
@@ -44,7 +46,7 @@ func ContentType(mediaType, fileType, videoCodec string) string {
case CodecAv1, "av1":
mediaType = header.ContentTypeWebmAv1
case CodecTheora, "ogg":
mediaType = header.ContentTypeTheora
mediaType = header.ContentTypeOggTheora
case string(fs.VideoWebm):
mediaType = header.ContentTypeWebm
}

View File

@@ -20,7 +20,7 @@ func TestContentType(t *testing.T) {
assert.Equal(t, fs.MimeTypeMp4, ContentType(fs.MimeTypeMp4, "", ""))
})
t.Run("Mp4_AVC", func(t *testing.T) {
assert.Equal(t, header.ContentTypeMp4Avc, ContentType(fs.MimeTypeMp4, "", CodecAvc))
assert.Equal(t, header.ContentTypeMp4AvcHigh, ContentType(fs.MimeTypeMp4, "", CodecAvc))
})
t.Run("Mp4_HVC", func(t *testing.T) {
assert.Equal(t, header.ContentTypeMp4Hevc, ContentType(fs.MimeTypeMp4, "", CodecHevc))

View File

@@ -28,7 +28,7 @@ func TestInfo(t *testing.T) {
info := NewInfo()
info.VideoMimeType = fs.MimeTypeMp4
info.VideoCodec = CodecAvc
assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, info.VideoContentType())
})
t.Run("VideoFileExt", func(t *testing.T) {
info := NewInfo()

View File

@@ -40,7 +40,7 @@ func TestProbeFile(t *testing.T) {
assert.Equal(t, media.Video, info.MediaType)
assert.Equal(t, CodecAvc, info.VideoCodec)
assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType)
assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, info.VideoContentType())
assert.Equal(t, "810.0081ms", info.Duration.String())
assert.InEpsilon(t, 0.81, info.Duration.Seconds(), 0.01)
assert.Equal(t, 1, info.Tracks)
@@ -67,7 +67,7 @@ func TestProbeFile(t *testing.T) {
assert.Equal(t, media.Video, info.MediaType)
assert.Equal(t, CodecAvc, info.VideoCodec)
assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType)
assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, info.VideoContentType())
assert.Equal(t, "1.066666666s", info.Duration.String())
assert.InEpsilon(t, 1.066, info.Duration.Seconds(), 0.01)
assert.Equal(t, 2, info.Tracks)
@@ -147,7 +147,7 @@ func TestProbeFile(t *testing.T) {
assert.Equal(t, media.Live, info.MediaType)
assert.Equal(t, CodecAvc, info.VideoCodec)
assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType)
assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, info.VideoContentType())
assert.Equal(t, "1.024s", info.Duration.String())
assert.InEpsilon(t, 1.024, info.Duration.Seconds(), 0.01)
assert.Equal(t, 2, info.Tracks)
@@ -180,7 +180,7 @@ func TestProbe(t *testing.T) {
assert.Equal(t, media.Video, info.MediaType)
assert.Equal(t, CodecAvc, info.VideoCodec)
assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType)
assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, info.VideoContentType())
assert.Equal(t, "810.0081ms", info.Duration.String())
assert.InEpsilon(t, 0.81, info.Duration.Seconds(), 0.01)
assert.Equal(t, 1, info.Tracks)
@@ -210,7 +210,7 @@ func TestProbe(t *testing.T) {
assert.Equal(t, media.Video, info.MediaType)
assert.Equal(t, CodecAvc, info.VideoCodec)
assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType)
assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, info.VideoContentType())
assert.Equal(t, "1.024s", info.Duration.String())
assert.InEpsilon(t, 1.024, info.Duration.Seconds(), 0.01)
assert.Equal(t, 2, info.Tracks)
@@ -240,7 +240,7 @@ func TestProbe(t *testing.T) {
assert.Equal(t, media.Live, info.MediaType)
assert.Equal(t, CodecAvc, info.VideoCodec)
assert.Equal(t, fs.MimeTypeMp4, info.VideoMimeType)
assert.Equal(t, header.ContentTypeMp4Avc, info.VideoContentType())
assert.Equal(t, header.ContentTypeMp4AvcHigh, info.VideoContentType())
assert.Equal(t, "1.024s", info.Duration.String())
assert.InEpsilon(t, 1.024, info.Duration.Seconds(), 0.01)
assert.Equal(t, 2, info.Tracks)

View File

@@ -15,7 +15,7 @@ var Unknown = Type{
var Mp4 = Type{
Codec: CodecAvc,
FileType: fs.VideoMp4,
ContentType: header.ContentTypeMp4Avc,
ContentType: header.ContentTypeMp4AvcHigh,
WidthLimit: 8192,
HeightLimit: 4320,
Public: true,
@@ -30,7 +30,8 @@ var Mov = Type{
Public: true,
}
// Avc specifies the MPEG-4 Advanced Video Coding (H.264) format.
// Avc specifies the MPEG-4 Advanced Video Coding (H.264) format,
// see https://en.wikipedia.org/wiki/Advanced_Video_Coding.
var Avc = Type{
Codec: CodecAvc,
FileType: fs.VideoAvc,