mirror of
https://github.com/photoprism/photoprism.git
synced 2025-09-26 21:01:58 +08:00
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -155,8 +155,11 @@ var Extensions = FileExtensions{
|
||||
".flv": VideoFlash,
|
||||
".f4v": VideoFlash,
|
||||
".mkv": VideoMkv,
|
||||
".ts": VideoM2TS,
|
||||
".m2t": VideoM2TS,
|
||||
".m2ts": VideoM2TS,
|
||||
".mp2t": VideoM2TS,
|
||||
".mts": VideoAvcHD,
|
||||
".m2ts": VideoBDAV,
|
||||
".ogv": VideoTheora,
|
||||
".ogg": VideoTheora,
|
||||
".ogx": VideoTheora,
|
||||
|
@@ -51,8 +51,8 @@ var TypeInfo = TypeMap{
|
||||
VideoMkv: "Matroska Multimedia Container",
|
||||
VideoMpeg: "Moving Picture Experts Group (MPEG)",
|
||||
VideoMjpeg: "Motion JPEG",
|
||||
VideoM2TS: "MPEG-2 Transport Stream (M2TS)",
|
||||
VideoAvcHD: "Advanced Video Coding High Definition (AVCHD)",
|
||||
VideoBDAV: "Blu-ray MPEG-2 Transport Stream",
|
||||
VideoTheora: "Ogg Media (OGG)",
|
||||
SidecarXMP: "Adobe Extensible Metadata Platform",
|
||||
SidecarAppleXml: "Apple Image Edits XML",
|
||||
|
@@ -87,8 +87,8 @@ const (
|
||||
Video3GP Type = "3gp" // Mobile Multimedia Container, MPEG-4 Part 12
|
||||
Video3G2 Type = "3g2" // Similar to 3GP, consumes less space & bandwidth
|
||||
VideoFlash Type = "flv" // Flash Video
|
||||
VideoM2TS Type = "m2t" // MPEG-2 Transport Stream (M2TS)
|
||||
VideoAvcHD Type = "mts" // AVCHD (Advanced Video Coding High Definition)
|
||||
VideoBDAV Type = "m2ts" // Blu-ray MPEG-2 Transport Stream
|
||||
VideoTheora Type = "ogv" // Ogg container format maintained by the Xiph.Org, free and open
|
||||
VideoASF Type = "asf" // Advanced Systems/Streaming Format (ASF)
|
||||
VideoAVI Type = "avi" // Microsoft Audio Video Interleave (AVI)
|
||||
|
@@ -29,6 +29,9 @@ func MimeType(filename string) (mimeType string) {
|
||||
// Determine mime type based on the extension for the following
|
||||
// formats, which otherwise cannot be reliably distinguished:
|
||||
switch fileType {
|
||||
// MPEG-2 Transport Stream
|
||||
case VideoM2TS, VideoAvcHD:
|
||||
return header.ContentTypeM2TS
|
||||
// Apple QuickTime Container
|
||||
case VideoMov:
|
||||
return header.ContentTypeMov
|
||||
|
@@ -56,8 +56,8 @@ var Formats = map[fs.Type]Type{
|
||||
fs.Video3GP: Video,
|
||||
fs.Video3G2: Video,
|
||||
fs.VideoFlash: Video,
|
||||
fs.VideoM2TS: Video,
|
||||
fs.VideoAvcHD: Video,
|
||||
fs.VideoBDAV: Video,
|
||||
fs.VideoTheora: Video,
|
||||
fs.VideoASF: Video,
|
||||
fs.VideoWMV: Video,
|
||||
|
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
// Standard ContentType strings for audio and video files:
|
||||
const (
|
||||
ContentTypeM2TS = "video/mp2t"
|
||||
ContentTypeM4v = "video/x-m4v"
|
||||
ContentTypeMp4 = "video/mp4"
|
||||
ContentTypeMp4Avc = ContentTypeMp4 + "; codecs=\"avc1\"" // MPEG-4 AVC (H.264)
|
||||
|
@@ -76,7 +76,6 @@ var CompatibleBrands = Chunks{
|
||||
ChunkHEV2,
|
||||
ChunkHEV3,
|
||||
ChunkDVHE,
|
||||
ChunkHEIC,
|
||||
ChunkAV01,
|
||||
ChunkAV1C,
|
||||
ChunkMMP4,
|
||||
|
@@ -60,13 +60,13 @@ func (c Chunk) FileOffset(fileName string) (int, error) {
|
||||
|
||||
defer file.Close()
|
||||
|
||||
index, err := c.DataOffset(file, -1)
|
||||
index, err := c.DataOffset(file, 0, -1)
|
||||
|
||||
return index, err
|
||||
}
|
||||
|
||||
// DataOffset returns the index of the chunk in file, or -1 if it was not found.
|
||||
func (c Chunk) DataOffset(file io.ReadSeeker, maxOffset int) (int, error) {
|
||||
func (c Chunk) DataOffset(file io.ReadSeeker, offset, maxOffset int) (int, error) {
|
||||
if file == nil {
|
||||
return -1, errors.New("file is nil")
|
||||
}
|
||||
@@ -79,8 +79,11 @@ func (c Chunk) DataOffset(file io.ReadSeeker, maxOffset int) (int, error) {
|
||||
// Create buffered read seeker.
|
||||
r := bufseekio.NewReadSeeker(file, blockSize, dataSize)
|
||||
|
||||
// Index offset.
|
||||
var offset int
|
||||
if seekOffset, seekErr := r.Seek(int64(offset), io.SeekStart); seekErr != nil {
|
||||
return -1, seekErr
|
||||
} else {
|
||||
offset = int(seekOffset)
|
||||
}
|
||||
|
||||
// Search in batches.
|
||||
for {
|
||||
|
@@ -39,6 +39,17 @@ func TestChunk_FileOffset(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 23213, index)
|
||||
})
|
||||
t.Run("motion-photo.heif", func(t *testing.T) {
|
||||
index, err := ChunkFTYP.FileOffset("testdata/motion-photo.heif")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 4, index)
|
||||
index, err = ChunkHEIC.FileOffset("testdata/motion-photo.heif")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 8, index)
|
||||
index, err = ChunkHVC1.FileOffset("testdata/motion-photo.heif")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 976016, index)
|
||||
})
|
||||
}
|
||||
|
||||
func TestChunks(t *testing.T) {
|
||||
|
@@ -33,6 +33,7 @@ const (
|
||||
CodecVp08 Codec = "vp08" // Google VP8
|
||||
CodecVp09 Codec = "vp09" // Google VP9
|
||||
CodecTheora Codec = "ogv" // Ogg Vorbis Video
|
||||
CodecM2TS Codec = "m2t" // MPEG-2 Transport Stream
|
||||
CodecWebm Codec = "webm" // Google WebM
|
||||
)
|
||||
|
||||
|
@@ -64,6 +64,8 @@ func ContentType(mediaType, fileType, videoCodec string, hdr bool) string {
|
||||
mediaType = header.ContentTypeMp4
|
||||
case fs.VideoMkv.Equal(fileType):
|
||||
mediaType = header.ContentTypeMkv
|
||||
case fs.VideoM2TS.Equal(fileType) || videoCodec == CodecM2TS:
|
||||
mediaType = header.ContentTypeM2TS
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -153,8 +153,7 @@ func Probe(file io.ReadSeeker) (info Info, err error) {
|
||||
// If no AVC video was found, search the video data for High Efficiency Video Coding (HEVC) chunks,
|
||||
// see https://stackoverflow.com/questions/63468587/what-hevc-codec-tag-to-use-with-fmp4-hvc1-or-hev1.
|
||||
if info.VideoCodec == "" {
|
||||
// To improve performance, only search for "hvc1" as that is the most common HEVC video identifier.
|
||||
if fileOffset, fileErr := ChunkHVC1.DataOffset(file, -1); fileOffset > 0 && fileErr == nil {
|
||||
if fileOffset, fileErr := ChunkHVC1.DataOffset(file, 0, -1); fileOffset > 0 && fileErr == nil {
|
||||
info.VideoCodec = CodecHvc1
|
||||
}
|
||||
}
|
||||
|
@@ -252,4 +252,34 @@ func TestProbe(t *testing.T) {
|
||||
assert.Equal(t, false, info.FastStart)
|
||||
assert.Equal(t, true, info.Compatible)
|
||||
})
|
||||
t.Run("motion-photo.heif", func(t *testing.T) {
|
||||
f, fileErr := os.Open("testdata/motion-photo.heif")
|
||||
require.NoError(t, fileErr)
|
||||
defer f.Close()
|
||||
|
||||
info, err := Probe(f)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, info)
|
||||
|
||||
assert.Equal(t, "", info.FileName)
|
||||
assert.Equal(t, int64(-1), info.FileSize)
|
||||
assert.Equal(t, fs.TypeUnknown, info.FileType)
|
||||
assert.Equal(t, Mp4, info.VideoType)
|
||||
assert.Equal(t, int64(978741), info.VideoOffset)
|
||||
assert.Equal(t, int64(0), info.ThumbOffset)
|
||||
assert.Equal(t, media.Live, info.MediaType)
|
||||
assert.Equal(t, CodecHvc1, info.VideoCodec)
|
||||
assert.Equal(t, header.ContentTypeMp4, info.VideoMimeType)
|
||||
assert.Equal(t, header.ContentTypeMp4HvcMain10, info.VideoContentType())
|
||||
assert.Equal(t, "2.9686s", info.Duration.String())
|
||||
assert.InEpsilon(t, 2.9686, info.Duration.Seconds(), 0.01)
|
||||
assert.Equal(t, 2, info.Tracks)
|
||||
assert.Equal(t, 0, info.VideoWidth)
|
||||
assert.Equal(t, 0, info.VideoHeight)
|
||||
assert.Equal(t, 89, info.Frames)
|
||||
assert.Equal(t, 30.0, info.FPS)
|
||||
assert.Equal(t, false, info.Encrypted)
|
||||
assert.Equal(t, false, info.FastStart)
|
||||
assert.Equal(t, true, info.Compatible)
|
||||
})
|
||||
}
|
||||
|
@@ -74,6 +74,9 @@ var Types = Standards{
|
||||
"mkv1": MkvAv1,
|
||||
"ogg": Theora, // ↓ Theora video in OGG container
|
||||
"ogv": Theora,
|
||||
"m2t": M2TS, // ↓ MPEG-2 Transport Stream container
|
||||
"m2ts": M2TS,
|
||||
"mp2t": M2TS,
|
||||
"mp4": Mp4, // ↓ Unknown codec in MP4 container
|
||||
"mpeg4": Mp4,
|
||||
"webm": Webm, // ↓ Unknown codec in WebM container
|
||||
|
BIN
pkg/media/video/testdata/bear.m2ts
vendored
Normal file
BIN
pkg/media/video/testdata/bear.m2ts
vendored
Normal file
Binary file not shown.
BIN
pkg/media/video/testdata/motion-photo.heif
vendored
Normal file
BIN
pkg/media/video/testdata/motion-photo.heif
vendored
Normal file
Binary file not shown.
@@ -21,6 +21,16 @@ var Mp4 = Type{
|
||||
Public: true,
|
||||
}
|
||||
|
||||
// M2TS specifies the MPEG-2 Transport Stream (M2TS) multimedia container format.
|
||||
var M2TS = Type{
|
||||
Codec: CodecAvc1,
|
||||
FileType: fs.VideoM2TS,
|
||||
ContentType: header.ContentTypeM2TS,
|
||||
WidthLimit: 8192,
|
||||
HeightLimit: 4320,
|
||||
Public: false,
|
||||
}
|
||||
|
||||
// Mov specifies the Apple QuickTime (QT) container format.
|
||||
var Mov = Type{
|
||||
Codec: CodecAvc1,
|
||||
|
Reference in New Issue
Block a user