Video: Transcoding to AVC1 #636 #513 #624 #603 #533 $440

This commit is contained in:
Michael Mayer 2020-12-05 04:24:10 +01:00
parent 0b16a67c90
commit 846c635f22
7 changed files with 128 additions and 9 deletions

View file

@ -249,7 +249,7 @@ export class Photo extends RestModel {
return false;
}
return this.Files.findIndex(f => f.Codec === CodecAvc1) !== -1 || this.Files.findIndex(f => f.Type === TypeMP4) !== -1;
return this.Files.findIndex(f => f.Video) !== -1;
}
videoFile() {

View file

@ -3,11 +3,12 @@ package api
import (
"net/http"
"github.com/photoprism/photoprism/internal/service"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/video"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -60,7 +61,7 @@ func GetVideo(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
if mf, err := photoprism.NewMediaFile(fileName); err != nil {
log.Errorf("video: file %s is missing", txt.Quote(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
@ -68,6 +69,18 @@ func GetVideo(router *gin.RouterGroup) {
logError("video", f.Update("FileMissing", true))
return
} else if !mf.IsPlayableVideo() {
conv := service.Convert()
avcFile, err := conv.ToAvc1(mf)
if err != nil {
log.Errorf("video: failed transcoding %s", txt.Quote(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
return
}
fileName = avcFile.FileName()
}
if c.Query("download") != "" {

View file

@ -286,3 +286,79 @@ func (c *Convert) ToJpeg(image *MediaFile) (*MediaFile, error) {
return NewMediaFile(jpegName)
}
// AvcConvertCommand returns the command for converting video files to AVC1.
func (c *Convert) AvcConvertCommand(mf *MediaFile, avcName string) (result *exec.Cmd, useMutex bool, err error) {
if mf.IsVideo() {
result = exec.Command(c.conf.FFmpegBin(), "-i", mf.FileName(), avcName)
} else {
return nil, useMutex, fmt.Errorf("convert: file type %s not supported in %s", mf.FileType(), txt.Quote(mf.BaseName()))
}
return result, useMutex, nil
}
// ToAvc1 converts a single video file to AVC1 if possible.
func (c *Convert) ToAvc1(video *MediaFile) (*MediaFile, error) {
if !video.Exists() {
return nil, fmt.Errorf("convert: can not convert to avc1, file does not exist (%s)", video.RelName(c.conf.OriginalsPath()))
}
if video.IsPlayableVideo() {
return video, nil
}
avcName := fs.TypeMp4.FindFirst(video.FileName(), []string{c.conf.SidecarPath(), fs.HiddenPath}, c.conf.OriginalsPath(), c.conf.Settings().Index.Sequences)
mediaFile, err := NewMediaFile(avcName)
if err == nil && mediaFile.IsPlayableVideo() {
return mediaFile, nil
}
if !c.conf.SidecarWritable() {
return nil, fmt.Errorf("convert: disabled in read only mode (%s)", video.RelName(c.conf.OriginalsPath()))
}
avcName = fs.FileName(video.FileName(), c.conf.SidecarPath(), c.conf.OriginalsPath(), fs.AvcExt, c.conf.Settings().Index.Sequences)
fileName := video.RelName(c.conf.OriginalsPath())
log.Debugf("convert: %s -> %s", fileName, filepath.Base(avcName))
event.Publish("index.converting", event.Data{
"fileType": video.FileType(),
"fileName": fileName,
"baseName": filepath.Base(fileName),
"xmpName": "",
})
cmd, useMutex, err := c.AvcConvertCommand(video, avcName)
if err != nil {
return nil, err
}
if useMutex {
// Make sure only one command is executed at a time.
// See https://photo.stackexchange.com/questions/105969/darktable-cli-fails-because-of-locked-database-file
c.cmdMutex.Lock()
defer c.cmdMutex.Unlock()
}
// Fetch command output.
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
// Run convert command.
if err := cmd.Run(); err != nil {
if stderr.String() != "" {
return nil, errors.New(stderr.String())
} else {
return nil, err
}
}
return NewMediaFile(avcName)
}

View file

@ -663,7 +663,7 @@ func (m *MediaFile) IsBitmap() bool {
// IsVideo returns true if this is a video file.
func (m *MediaFile) IsVideo() bool {
return strings.HasPrefix(m.MimeType(), "video/")
return strings.HasPrefix(m.MimeType(), "video/") || m.MediaType() == fs.MediaVideo
}
// IsJson return true if this media file is a json sidecar file.
@ -730,7 +730,7 @@ func (m *MediaFile) IsSidecar() bool {
// IsPlayableVideo returns true if this is a supported video file format.
func (m *MediaFile) IsPlayableVideo() bool {
return m.IsVideo() && m.HasFileType(fs.TypeMP4)
return m.IsVideo() && m.HasFileType(fs.TypeMp4)
}
// IsPhoto returns true if this file is a photo / image.

View file

@ -45,7 +45,7 @@ type Type struct {
type TypeMap map[string]Type
var TypeMP4 = Type{
Format: fs.TypeMP4,
Format: fs.TypeMp4,
Width: 0,
Height: 0,
Public: true,

View file

@ -20,8 +20,17 @@ const (
TypeRaw FileType = "raw" // RAW image file.
TypeHEIF FileType = "heif" // High Efficiency Image File Format
TypeMov FileType = "mov" // Video files.
TypeMP4 FileType = "mp4"
TypeMp4 FileType = "mp4"
TypeHEVC FileType = "hevc"
TypeAvi FileType = "avi"
Type3gp FileType = "3gp"
Type3g2 FileType = "3g2"
TypeFlv FileType = "flv"
TypeMkv FileType = "mkv"
TypeMpg FileType = "mpg"
TypeOgv FileType = "ogv"
TypeWebm FileType = "webm"
TypeWMV FileType = "wmv"
TypeXMP FileType = "xmp" // Adobe XMP sidecar file (XML).
TypeAAE FileType = "aae" // Apple sidecar file (XML).
TypeXML FileType = "xml" // XML metadata / config / sidecar file.
@ -39,6 +48,8 @@ type TypeExtensions map[FileType][]string
const (
YamlExt = ".yml"
JpegExt = ".jpg"
AvcExt = ".mp4"
HevcExt = ".hevc"
)
// FileExt contains the filename extensions of file formats known to PhotoPrism.
@ -57,7 +68,17 @@ var FileExt = FileExtensions{
".dng": TypeRaw,
".mov": TypeMov,
".avi": TypeAvi,
".mp4": TypeMP4,
".mp4": TypeMp4,
".hevc": TypeHEVC,
".3gp": Type3gp,
".3g2": Type3g2,
".flv": TypeFlv,
".mkv": TypeMkv,
".mpg": TypeMpg,
".mpeg": TypeMpg,
".ogv": TypeOgv,
".webm": TypeWebm,
".wmv": TypeWMV,
".yml": TypeYaml,
".yaml": TypeYaml,
".jpg": TypeJpeg,

View file

@ -19,8 +19,17 @@ var MediaTypes = map[FileType]MediaType{
TypeBitmap: MediaImage,
TypeHEIF: MediaImage,
TypeAvi: MediaVideo,
TypeMP4: MediaVideo,
TypeHEVC: MediaVideo,
TypeMp4: MediaVideo,
TypeMov: MediaVideo,
Type3gp: MediaVideo,
Type3g2: MediaVideo,
TypeFlv: MediaVideo,
TypeMkv: MediaVideo,
TypeMpg: MediaVideo,
TypeOgv: MediaVideo,
TypeWebm: MediaVideo,
TypeWMV: MediaVideo,
TypeXMP: MediaSidecar,
TypeXML: MediaSidecar,
TypeAAE: MediaSidecar,