photoprism/pkg/video/chunk.go

109 lines
1.9 KiB
Go
Raw Normal View History

package video
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"os"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/sunfish-shogi/bufseekio"
)
// Chunk represents a fixed length file chunk.
type Chunk [4]byte
// Get returns the chunk as byte array.
func (c Chunk) Get() [4]byte {
return c
}
// Hex returns the chunk as hex formatted string.
func (c Chunk) Hex() string {
return fmt.Sprintf("0x%x", c[:])
}
// String returns the chunk as string.
func (c Chunk) String() string {
return string(c[:])
}
// Bytes returns the chunk as byte slice.
func (c Chunk) Bytes() []byte {
return c[:]
}
// Uint32 returns the chunk as unsigned integer.
func (c Chunk) Uint32() uint32 {
return binary.BigEndian.Uint32(c.Bytes())
}
// Equal compares the chunk with a byte slice.
func (c Chunk) Equal(b []byte) bool {
return bytes.Equal(c.Bytes(), b)
}
// FileOffset returns the index of the chunk, or -1 if it was not found.
func (c Chunk) FileOffset(fileName string) (int, error) {
if !fs.FileExists(fileName) {
return -1, errors.New("file not found")
}
file, err := os.Open(fileName)
if err != nil {
return -1, err
}
defer file.Close()
index, err := c.DataOffset(file)
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) (int, error) {
if file == nil {
return -1, errors.New("file is nil")
}
data := c.Bytes()
dataSize := len(data)
blockSize := 128 * 1024
buffer := make([]byte, blockSize)
// Create buffered read seeker.
r := bufseekio.NewReadSeeker(file, blockSize, dataSize)
// 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
}
// Return data index, if found.
if i := bytes.Index(buffer, data); i >= 0 {
return offset + i, nil
}
offset += n
}
return -1, nil
}