photoprism/pkg/video/chunks.go
Michael Mayer 2339197311 Live Photos: Add support for playing videos embedded in HEIC images #439
Related Issues:
- Samsung: Initial support for Motion Photos (#439)
- Google: Initial support for Motion Photos (#1739)
- Metadata: Flag Samsung/Google Motion Photos as Live Photos (#2788)

Signed-off-by: Michael Mayer <michael@photoprism.app>
2023-09-23 11:27:20 +02:00

93 lines
1.6 KiB
Go

package video
import (
"bytes"
"errors"
"io"
"github.com/sunfish-shogi/bufseekio"
)
// Chunks represents a list of file chunks.
type Chunks []Chunk
// Contains tests if the chunk is contained in this list.
func (c Chunks) Contains(s [4]byte) bool {
if len(c) == 0 {
return false
}
// Find matches.
for i := range c {
if s == c[i] {
return true
}
}
return false
}
// ContainsAny checks if at least one common chunk exists in this list.
func (c Chunks) ContainsAny(b [][4]byte) bool {
if len(c) == 0 || len(b) == 0 {
return false
}
// Find matches.
for i := range c {
for j := range b {
if b[j] == c[i] {
return true
}
}
}
// Not found.
return false
}
// FileTypeOffset returns the file type start offset in f, or -1 if it was not found.
func (c Chunks) FileTypeOffset(file io.ReadSeeker) (int, error) {
if file == nil {
return -1, errors.New("file is nil")
}
ftyp := ChunkFTYP.Bytes()
blockSize := 128 * 1024
buffer := make([]byte, blockSize)
// Create buffered read seeker.
r := bufseekio.NewReadSeeker(file, blockSize, 8)
// Index offset.
var offset int
// Search in batches.
for {
n, err := r.Read(buffer)
buffer = buffer[:n]
if err != nil {
if err != io.EOF {
return -1, err
}
break
} else if n == 0 {
break
}
// Find ftyp chunk.
if i := bytes.Index(buffer, ftyp); i < 0 {
// Not found.
} else if j := i + 4; j < 8 || len(buffer) < j+4 {
// Skip.
} else if k := j + 4; c.Contains(*(*[4]byte)(buffer[j:k])) {
return offset + i - 4, nil
}
offset += n
}
return -1, nil
}