Skip to content

Commit 501ef56

Browse files
committed
Videos: Support for detection and streaming of additional formats photoprism#4770
Signed-off-by: Michael Mayer <[email protected]>
1 parent 7cfd09c commit 501ef56

33 files changed

+664
-379
lines changed

frontend/src/common/can.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,22 @@ Additional information can be found in our Developer Guide:
2525

2626
import * as media from "common/media";
2727

28+
// see https://tools.woolyss.com/html5-canplaytype-tester/
2829
export const useVideo = !!document.createElement("video").canPlayType;
29-
export const useAVC = useVideo // AVC
30+
export const useMp4Avc = useVideo // AVC
3031
? !!document.createElement("video").canPlayType(media.ContentTypeMp4AvcMain)
3132
: false;
32-
export const useHEVC = useVideo // HEVC, Basic Support
33-
? !!document.createElement("video").canPlayType(media.ContentTypeMp4HvcMain)
33+
export const useMp4Hvc = useVideo // HEVC, Basic Support
34+
? !!document.createElement("video").canPlayType(media.ContentTypeMp4HvcMain10)
3435
: false;
35-
export const useHEV1 = useVideo // HEV1, Basic Support
36-
? !!document.createElement("video").canPlayType(media.ContentTypeMp4HevMain)
36+
export const useMp4Hev = useVideo // HEV1, Basic Support
37+
? !!document.createElement("video").canPlayType(media.ContentTypeMp4HevMain10)
3738
: false;
38-
export const useVVC = useVideo // VVC, Basic Support
39+
export const useMp4Vvc = useVideo // VVC, Basic Support
3940
? !!document.createElement("video").canPlayType(media.ContentTypeMp4Vvc)
4041
: false;
41-
export const useOGV = useVideo // Ogg Theora
42-
? !!document.createElement("video").canPlayType(media.ContentTypeOgg)
42+
export const useMp4Evc = useVideo // EVC, Basic Support
43+
? !!document.createElement("video").canPlayType(media.ContentTypeMp4Evc)
4344
: false;
4445
export const useWebM = useVideo // Google WebM
4546
? !!document.createElement("video").canPlayType(media.ContentTypeWebm)
@@ -50,6 +51,15 @@ export const useVP8 = useVideo // Google WebM, VP8
5051
export const useVP9 = useVideo // Google WebM, VP9
5152
? !!document.createElement("video").canPlayType(media.ContentTypeWebmVp9)
5253
: false;
53-
export const useAV1 = useVideo // AV1, Main Profile
54-
? !!document.createElement("video").canPlayType(media.ContentTypeWebmAv1)
54+
export const useMp4Av1 = useVideo // AV1 in MP4, Main Profile 10-bit HDR
55+
? !!document.createElement("video").canPlayType(media.ContentTypeMp4Av1Main10)
56+
: false;
57+
export const useWebmAv1 = useVideo // AV1 in WebM, Main Profile 10-bit HDR
58+
? !!document.createElement("video").canPlayType(media.ContentTypeWebmAv1Main10)
59+
: false;
60+
export const useMkvAv1 = useVideo // AV1 in MKV, Main Profile 10-bit HDR
61+
? !!document.createElement("video").canPlayType(media.ContentTypeMkvAv1Main10)
62+
: false;
63+
export const useTheora = useVideo // Ogg Theora
64+
? !!document.createElement("video").canPlayType(media.ContentTypeOgg)
5565
: false;

frontend/src/common/media.js

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Media types.
1+
// Media types supported by PhotoPrism:
22
export const Animated = "animated";
33
export const Audio = "audio";
44
export const Document = "document";
@@ -9,53 +9,69 @@ export const Live = "live";
99
export const Vector = "vector";
1010
export const Video = "video";
1111

12-
// Video codec names.
13-
//
14-
// Browser support can be tested by visiting one of the following sites:
15-
// - https://ott.dolby.com/codec_test/index.html
16-
// - https://dmnsgn.github.io/media-codecs/
17-
// - https://cconcolato.github.io/media-mime-support/
18-
// - https://thorium.rocks/misc/h265-tester.html
19-
export const CodecAvc = "avc1";
12+
// Video codec names, see https://mp4ra.org/registered-types/codecs:
13+
export const CodecAvc1 = "avc1";
2014
export const CodecAvc3 = "avc3";
21-
export const CodecHvc = "hvc1";
22-
export const CodecHev = "hev1";
23-
export const CodecVvc = "vvc1";
24-
export const CodecEvc = "evc1";
15+
export const CodecAvc4 = "avc4";
16+
export const CodecHvc1 = "hvc1";
17+
export const CodecHev1 = "hev1";
18+
export const CodecVvc1 = "vvc1";
19+
export const CodecEvc1 = "evc1";
2520
export const CodecTheora = "ogv";
26-
export const CodecVp8 = "vp8";
27-
export const CodecVp9 = "vp09";
21+
export const CodecVp08 = "vp08";
22+
export const CodecVp09 = "vp09";
2823
export const CodecAv1 = "av01";
2924
export const CodecAv1C = "av1c";
3025

31-
// Media file formats.
26+
// Video file formats:
3227
export const FormatMp4 = "mp4";
3328
export const FormatAvc = "avc";
3429
export const FormatHvc = "hvc";
3530
export const FormatHev = "hev";
3631
export const FormatVvc = "vvc";
3732
export const FormatEvc = "evc";
38-
export const FormatTheora = "ogg";
3933
export const FormatWebm = "webm";
4034
export const FormatVp8 = "vp8";
4135
export const FormatVp9 = "vp9";
4236
export const FormatAv1 = "av1";
37+
export const FormatWebmAv1 = "webm_av1";
38+
export const FormatMkvAv1 = "mkv_av1";
39+
export const FormatTheora = "ogg";
4340
export const FormatWebp = "webp";
41+
42+
// Image file formats:
4443
export const FormatJpeg = "jpg";
4544
export const FormatJpegXL = "jxl";
4645
export const FormatPng = "png";
4746
export const FormatGif = "gif";
47+
48+
// Vector file formats:
4849
export const FormatSVG = "svg";
4950

50-
// HTTP Content types (MIME).
51+
// Content type strings for common media formats, see https://tools.woolyss.com/html5-canplaytype-tester/:
5152
export const ContentTypeMp4 = "video/mp4";
5253
export const ContentTypeMp4AvcMain = ContentTypeMp4 + '; codecs="avc1.4d0028"'; // AVC High Profile Level 4
5354
export const ContentTypeMp4HvcMain = ContentTypeMp4 + '; codecs="hvc1.1.6.L93.B0"';
55+
export const ContentTypeMp4HvcMain10 = ContentTypeMp4 + '; codecs="hvc1.2.4.L153.B0"';
5456
export const ContentTypeMp4HevMain = ContentTypeMp4 + '; codecs="hev1.1.6.L93.B0"';
57+
export const ContentTypeMp4HevMain10 = ContentTypeMp4 + '; codecs="hev1.2.4.L153.B0'; // MPEG-4 HEVC Bitstream, Main 10 Profile, not supported on macOS
5558
export const ContentTypeMp4Vvc = ContentTypeMp4 + '; codecs="vvc1"';
59+
export const ContentTypeMp4Evc = ContentTypeMp4 + '; codecs="evc1"';
60+
export const ContentTypeMp4Av1 = ContentTypeMp4 + '; codecs="av01"'; // AV1 in MP4 container
61+
export const ContentTypeMp4Av1Main = ContentTypeMp4 + '; codecs="av01.0.08M.08"'; // AV1 Main Profile, level 4.0, High tier, 8 bits
62+
export const ContentTypeMp4Av1Main10 = ContentTypeMp4 + '; codecs="av01.0.08H.10"'; // AV1 Main Profile, level 4.0, High tier, 10 bits
63+
export const ContentTypeMp4Av1Main12 = ContentTypeMp4 + '; codecs="av01.0.08H.12"'; // AV1 Main Profile, level 4.0, High tier, 12 bits
5664
export const ContentTypeOgg = "video/ogg";
5765
export const ContentTypeOggTheora = ContentTypeOgg + '; codecs="theora, vorbis"';
5866
export const ContentTypeWebm = "video/webm";
5967
export const ContentTypeWebmVp8 = ContentTypeWebm + '; codecs="vp8"';
6068
export const ContentTypeWebmVp9 = ContentTypeWebm + '; codecs="vp09.00.10.08"';
61-
export const ContentTypeWebmAv1 = ContentTypeWebm + '; codecs="av01.2.10M.10"';
69+
export const ContentTypeWebmAv1 = ContentTypeWebm + '; codecs="av01"'; // AV1 in WebM container
70+
export const ContentTypeWebmAv1Main = ContentTypeWebm + '; codecs="av01.0.08M.08"'; // AV1 Main Profile, level 4.0, High tier, 8 bits
71+
export const ContentTypeWebmAv1Main10 = ContentTypeWebm + '; codecs="av01.0.08H.10"'; // AV1 Main Profile, level 4.0, High tier, 10 bits
72+
export const ContentTypeWebmAv1Main12 = ContentTypeWebm + '; codecs="av01.0.08H.12"'; // AV1 Main Profile, level 4.0, High tier, 12 bits
73+
export const ContentTypeMkv = "video/matroska";
74+
export const ContentTypeMkvAv1 = ContentTypeMkv + '; codecs="av01"'; // AV1 in MKV container
75+
export const ContentTypeMkvAv1Main = ContentTypeMkv + '; codecs="av01.0.08M.08"'; // AV1 Main Profile, level 4.0, High tier, 8 bits
76+
export const ContentTypeMkvAv1Main10 = ContentTypeMkv + '; codecs="av01.0.08H.10"'; // AV1 Main Profile, level 4.0, High tier, 10 bits
77+
export const ContentTypeMkvAv1Main12 = ContentTypeMkv + '; codecs="av01.0.08H.12"'; // AV1 Main Profile, level 4.0, High tier, 12 bits

frontend/src/common/util.js

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export default class Util {
269269
return "GIF";
270270
case "dng":
271271
return "Adobe Digital Negative";
272-
case media.CodecAvc:
272+
case media.CodecAvc1:
273273
case media.FormatAvc:
274274
return "Advanced Video Coding (AVC) / H.264";
275275
case media.CodecAvc3:
@@ -280,14 +280,14 @@ export default class Util {
280280
return "AVIF Image Sequence";
281281
case "hev":
282282
case "hvc":
283-
case media.CodecHvc:
283+
case media.CodecHvc1:
284284
case media.FormatHvc:
285285
return "High Efficiency Video Coding (HEVC) / H.265";
286-
case media.CodecHev:
286+
case media.CodecHev1:
287287
case media.FormatHev:
288288
return "High Efficiency Video Coding (HEVC) Bitstream";
289289
case media.FormatEvc:
290-
case media.CodecEvc:
290+
case media.CodecEvc1:
291291
return "Essential Video Coding (MPEG-5 Part 1)";
292292
case "m4v":
293293
return "Apple iTunes Multimedia Container";
@@ -301,10 +301,10 @@ export default class Util {
301301
return "Google WebP";
302302
case media.FormatWebm:
303303
return "Google WebM";
304-
case media.CodecVp8:
304+
case media.CodecVp08:
305305
case media.FormatVp8:
306306
return "Google VP8";
307-
case media.CodecVp9:
307+
case media.CodecVp09:
308308
case media.FormatVp9:
309309
return "Google VP9";
310310
case "flv":
@@ -362,28 +362,29 @@ export default class Util {
362362
case media.CodecAv1C:
363363
case media.CodecAv1:
364364
return "AV1";
365-
case media.CodecAvc:
365+
case media.CodecAvc1:
366366
case media.CodecAvc3:
367+
case media.CodecAvc4:
367368
case media.FormatAvc:
368369
return "AVC";
369370
case "hvc":
370-
case media.CodecHev:
371+
case media.CodecHev1:
371372
case media.FormatHev:
372-
case media.CodecHvc:
373+
case media.CodecHvc1:
373374
case media.FormatHvc:
374375
return "HEVC";
375-
case media.CodecVvc:
376+
case media.CodecVvc1:
376377
case media.FormatVvc:
377378
return "VVC";
378-
case media.CodecEvc:
379+
case media.CodecEvc1:
379380
case media.FormatEvc:
380381
return "EVC";
381382
case media.FormatWebm:
382383
return "WebM";
383-
case media.CodecVp8:
384+
case media.CodecVp08:
384385
case media.FormatVp8:
385386
return "VP8";
386-
case media.CodecVp9:
387+
case media.CodecVp09:
387388
case media.FormatVp9:
388389
return "VP9";
389390
case "extended webp":
@@ -407,23 +408,23 @@ export default class Util {
407408
case "qt ":
408409
return "Apple QuickTime (MOV)";
409410
case "avc":
410-
case media.CodecAvc:
411+
case media.CodecAvc1:
411412
return "Advanced Video Coding (AVC) / H.264";
412413
case media.CodecAvc3:
413414
return "Advanced Video Coding (AVC) Bitstream";
414415
case "hvc":
415416
case "hev":
416-
case media.CodecHvc:
417+
case media.CodecHvc1:
417418
case media.FormatHvc:
418419
return "High Efficiency Video Coding (HEVC) / H.265";
419-
case media.CodecHev:
420+
case media.CodecHev1:
420421
case media.FormatHev:
421422
return "High Efficiency Video Coding (HEVC) Bitstream";
422423
case media.FormatVvc:
423-
case media.CodecVvc:
424+
case media.CodecVvc1:
424425
return "Versatile Video Coding (VVC) / H.266";
425426
case media.FormatEvc:
426-
case media.CodecEvc:
427+
case media.CodecEvc1:
427428
return "Essential Video Coding (MPEG-5 Part 1)";
428429
case "av1":
429430
case "av1c":
@@ -576,31 +577,28 @@ export default class Util {
576577
static videoFormat(codec, mime) {
577578
if ((!codec && !mime) || mime?.startsWith('video/mp4; codecs="avc')) {
578579
return media.FormatAvc;
579-
} else if (can.useHEVC && (codec === media.CodecHvc || mime?.startsWith('video/mp4; codecs="hvc'))) {
580-
return media.FormatHvc;
581-
} else if (can.useHEV1 && (codec === media.CodecHev || mime?.startsWith('video/mp4; codecs="hev'))) {
582-
return media.FormatHev; // HEVC Bitstream
583-
} else if (
584-
can.useVVC &&
585-
(codec === media.CodecVvc || codec === media.FormatVvc || mime?.startsWith('video/mp4; codecs="vvc'))
586-
) {
580+
} else if (can.useMp4Hvc && (codec === media.CodecHvc1 || mime?.startsWith('video/mp4; codecs="hvc'))) {
581+
return media.FormatHvc; // HEVC video with parameter sets not in the Samples
582+
} else if (can.useMp4Hev && (codec === media.CodecHev1 || mime?.startsWith('video/mp4; codecs="hev'))) {
583+
return media.FormatHev; // HEVC video with parameter sets also in the Samples, won't play on macOS
584+
} else if (can.useMp4Vvc && (codec === media.CodecVvc1 || mime?.startsWith('video/mp4; codecs="vvc'))) {
587585
return media.FormatVvc;
588-
} else if (can.useOGV && (codec === media.CodecTheora || codec === media.FormatTheora || mime === media.ContentTypeOgg)) {
589-
return media.FormatTheora;
590-
} else if (can.useVP8 && (codec === "vp8" || codec === "vp08" || mime?.startsWith('video/mp4; codecs="vp8'))) {
586+
} else if (can.useMp4Evc && (codec === media.CodecEvc1 || mime?.startsWith('video/mp4; codecs="evc'))) {
587+
return media.FormatEvc;
588+
} else if (can.useVP8 && (codec === media.CodecVp08 || mime?.startsWith('video/mp4; codecs="vp8'))) {
591589
return media.FormatVp8;
592-
} else if (can.useVP9 && (codec === "vp9" || codec === "vp09" || mime?.startsWith('video/mp4; codecs="vp09'))) {
590+
} else if (can.useVP9 && (codec === media.CodecVp09 || mime?.startsWith('video/mp4; codecs="vp09'))) {
593591
return media.FormatVp9;
594-
} else if (
595-
can.useAV1 &&
596-
(codec === media.CodecAv1 ||
597-
codec === media.CodecAv1C ||
598-
codec === media.FormatAv1 ||
599-
mime?.startsWith('video/webm; codecs="av01'))
600-
) {
592+
} else if (can.useMp4Av1 && (mime?.startsWith('video/mp4; codecs="av01') || mime?.startsWith("video/AV1"))) {
601593
return media.FormatAv1;
594+
} else if (can.useWebmAv1 && mime?.startsWith('video/webm; codecs="av01')) {
595+
return media.FormatWebmAv1;
596+
} else if (can.useMkvAv1 && mime?.startsWith('video/matroska; codecs="av01')) {
597+
return media.FormatMkvAv1;
602598
} else if (can.useWebM && (codec === media.FormatWebm || mime === media.ContentTypeWebm)) {
603599
return media.FormatWebm;
600+
} else if (can.useTheora && (codec === media.CodecTheora || mime === media.ContentTypeOgg)) {
601+
return media.FormatTheora;
604602
}
605603

606604
return media.FormatAvc;
@@ -626,28 +624,24 @@ export default class Util {
626624
switch (this.videoFormat(codec, mime)) {
627625
case media.FormatAvc:
628626
return media.ContentTypeMp4AvcMain;
629-
case media.CodecTheora:
630-
return media.ContentTypeOgg;
631-
case media.CodecVp8:
632-
case media.FormatVp8:
633-
return media.ContentTypeWebmVp8;
634-
case media.CodecVp9:
635-
case media.FormatVp9:
636-
return media.ContentTypeWebmVp9;
637-
case media.CodecAv1C:
638-
case media.CodecAv1:
639-
case media.FormatAv1:
640-
return media.ContentTypeWebmAv1;
641-
case media.FormatWebm:
642-
return media.ContentTypeWebm;
643-
case media.CodecHvc:
644627
case media.FormatHvc:
645628
return media.ContentTypeMp4HvcMain;
646-
case media.CodecHev:
647629
case media.FormatHev:
648630
return media.ContentTypeMp4HevMain;
649631
case media.FormatVvc:
650632
return media.ContentTypeMp4Vvc;
633+
case media.FormatVp8:
634+
return media.ContentTypeWebmVp8;
635+
case media.FormatVp9:
636+
return media.ContentTypeWebmVp9;
637+
case media.FormatWebmAv1:
638+
return media.ContentTypeWebmAv1Main10;
639+
case media.FormatMkvAv1:
640+
return media.ContentTypeMkvAv1Main10;
641+
case media.FormatWebm:
642+
return media.ContentTypeWebm;
643+
case media.FormatTheora:
644+
return media.ContentTypeOgg;
651645
default:
652646
return "video/mp4";
653647
}

frontend/src/model/model.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Additional information can be found in our Developer Guide:
2424
*/
2525

2626
export class Model {
27-
constructor(values) {
27+
constructor(values = false) {
2828
this.__originalValues = {};
2929

3030
if (values) {

frontend/src/model/photo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ export class Photo extends RestModel {
494494
return false;
495495
}
496496

497-
let file = files.find((f) => f.Codec === media.CodecAvc);
497+
let file = files.find((f) => f.Codec === media.CodecAvc1);
498498

499499
if (!file) {
500500
file = files.find((f) => f.FileType === media.FormatMp4);

0 commit comments

Comments
 (0)