From 4303b71311954b593aa52f490c3915a95e92bcd4 Mon Sep 17 00:00:00 2001 From: Googol Lee Date: Fri, 11 Oct 2024 22:27:56 +0200 Subject: [PATCH] Use hardware acceleration when converting videos. (#1056) --- Dockerfile | 20 ++- README.md | 8 ++ api/example.env | 5 + .../executable_worker/executable_worker.go | 92 +++--------- .../executable_worker_test.go | 33 +++++ .../executable_worker/ffmpeg_cli.go | 108 ++++++++++++++ .../executable_worker/ffmpeg_cli_test.go | 133 ++++++++++++++++++ .../executable_worker/testdata/bin/ffmpeg | 18 +++ .../executable_worker/testdata/bin/ffprobe | 18 +++ api/scanner/media_type/media_type.go | 2 +- .../processing_tasks/process_video_task.go | 6 +- api/utils/environment_variables.go | 7 +- dev-compose.yaml | 4 +- .../docker-compose.example.yml | 4 + docker-compose example/example.env | 7 + 15 files changed, 375 insertions(+), 90 deletions(-) create mode 100644 api/scanner/media_encoding/executable_worker/ffmpeg_cli.go create mode 100644 api/scanner/media_encoding/executable_worker/ffmpeg_cli_test.go create mode 100755 api/scanner/media_encoding/executable_worker/testdata/bin/ffmpeg create mode 100755 api/scanner/media_encoding/executable_worker/testdata/bin/ffprobe diff --git a/Dockerfile b/Dockerfile index 7900378e..ba8f98a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,7 +56,17 @@ COPY scripts/set_compiler_env.sh /app/scripts/ RUN chmod +x /app/scripts/*.sh \ && source /app/scripts/set_compiler_env.sh +COPY scripts/install_*.sh /app/scripts/ +# Split values in `/env` +# hadolint ignore=SC2046 +RUN chmod +x /app/scripts/*.sh \ + && export $(cat /env) \ + && /app/scripts/install_build_dependencies.sh \ + && /app/scripts/install_runtime_dependencies.sh + COPY --from=viktorstrate/dependencies /artifacts.tar.gz /dependencies/ +# Split values in `/env` +# hadolint ignore=SC2046 RUN export $(cat /env) \ && cd /dependencies/ \ && tar xfv artifacts.tar.gz \ @@ -67,13 +77,9 @@ RUN export $(cat /env) \ && ldconfig \ && apt-get install -y ./deb/jellyfin-ffmpeg.deb -COPY scripts/install_*.sh /app/scripts/ -RUN chmod +x /app/scripts/*.sh \ - && export $(cat /env) \ - && /app/scripts/install_build_dependencies.sh \ - && /app/scripts/install_runtime_dependencies.sh - COPY api/go.mod api/go.sum /app/api/ +# Split values in `/env` +# hadolint ignore=SC2046 RUN export $(cat /env) \ && go env \ && go mod download \ @@ -85,6 +91,8 @@ RUN export $(cat /env) \ github.com/Kagami/go-face COPY api /app/api +# Split values in `/env` +# hadolint ignore=SC2046 RUN export $(cat /env) \ && go env \ && go build -v -o photoview . diff --git a/README.md b/README.md index 01c76782..f00a7832 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,14 @@ Possible ways of securing a self-hosted service might be (but not limited to): Setting up and configuring of all these protections depends on and requires a lot of info about your local network and self-hosted services. Based on this info, the configuration flow and resulting services architecture might differ a lot between cases. That is why in the scope of this project, we can only provide you with this high-level list of possible ways of webservice protection. You'll need to investigate them, find the best combination and configuration for your case, and take responsibility to configure everything in the correct and consistent way. We cannot provide you support for such highly secured setups, as a lot of things might work differently because of security limitations. +### Hardware Acceleration + +It is possible to run the FFmpeg with a codec supproting the hardware acceleration, by defining `PHOTOVIEW_VIDEO_HARDWARE_ACCELERATION`. The value should be one of `qsv`, `vaapi`, `nvenc`. + +We only verified the hardware acceleration with `qsv` on an Intel chip. To let it work, it must map `/dev/dri` devices and set a ENV `PHOTOVIEW_VIDEO_HARDWARE_ACCELERATION=qsv`. See [docker-compose.example.yml](./docker-compose example/docker-compose.example.yml). + +If you verify other hardware accelerations working well, let us know. + ## Contributing 🎉 First off, thanks for your interest in contribution! 🎉 diff --git a/api/example.env b/api/example.env index fe6bf2f2..4db07714 100644 --- a/api/example.env +++ b/api/example.env @@ -32,3 +32,8 @@ PHOTOVIEW_SERVE_UI=0 # Set to 1 to set server in development mode, this enables graphql playground # Remove this if running in production PHOTOVIEW_DEVELOPMENT_MODE=1 + +# Set the hardware acceleration when encoding videos. +# Support `qsv`, `vaapi`, `nvenc`. +# Only `qsv` is verified with `/dev/dri//dev/dri` devices. +# PHOTOVIEW_VIDEO_HARDWARE_ACCELERATION= diff --git a/api/scanner/media_encoding/executable_worker/executable_worker.go b/api/scanner/media_encoding/executable_worker/executable_worker.go index c2b9b6d6..6c1c813b 100644 --- a/api/scanner/media_encoding/executable_worker/executable_worker.go +++ b/api/scanner/media_encoding/executable_worker/executable_worker.go @@ -6,96 +6,38 @@ import ( "os/exec" "strings" - "github.com/photoview/photoview/api/utils" - "github.com/pkg/errors" "gopkg.in/vansante/go-ffprobe.v2" ) func InitializeExecutableWorkers() { Magick = newMagickCli() - FfmpegCli = newFfmpegWorker() + Ffmpeg = newFfmpegCli() + + if err := SetFfprobePath(); err != nil { + log.Println("ffprobe init fail:", err) + } } var Magick *MagickCli = nil -var FfmpegCli *FfmpegWorker = nil +var Ffmpeg *FfmpegCli = nil type ExecutableWorker interface { Path() string } -type FfmpegWorker struct { - path string -} - -func newFfmpegWorker() *FfmpegWorker { - if utils.EnvDisableVideoEncoding.GetBool() { - log.Printf("Executable worker disabled (%s=1): ffmpeg\n", utils.EnvDisableVideoEncoding.GetName()) - return nil - } - - path, err := exec.LookPath("ffmpeg") +func SetFfprobePath() error { + path, err := exec.LookPath("ffprobe") if err != nil { - log.Println("Executable worker not found: ffmpeg") - } else { - version, err := exec.Command(path, "-version").Output() - if err != nil { - log.Printf("Error getting version of ffmpeg: %s\n", err) - return nil - } - - log.Printf("Found executable worker: ffmpeg (%s)\n", strings.Split(string(version), "\n")[0]) - - return &FfmpegWorker{ - path: path, - } - } - - return nil -} - -func (worker *FfmpegWorker) IsInstalled() bool { - return worker != nil -} - -func (worker *FfmpegWorker) EncodeMp4(inputPath string, outputPath string) error { - args := []string{ - "-i", - inputPath, - "-vcodec", "h264", - "-acodec", "aac", - "-vf", "scale='min(1080,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2", - "-movflags", "+faststart+use_metadata_tags", - outputPath, - } - - cmd := exec.Command(worker.path, args...) - - if err := cmd.Run(); err != nil { - return errors.Wrapf(err, "encoding video using: %s", worker.path) - } - - return nil -} - -func (worker *FfmpegWorker) EncodeVideoThumbnail(inputPath string, outputPath string, probeData *ffprobe.ProbeData) error { - - thumbnailOffsetSeconds := fmt.Sprintf("%d", int(probeData.Format.DurationSeconds*0.25)) - - args := []string{ - "-ss", thumbnailOffsetSeconds, // grab frame at time offset - "-i", - inputPath, - "-vframes", "1", // output one frame - "-an", // disable audio - "-vf", "scale='min(1024,iw)':'min(1024,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2", - outputPath, - } - - cmd := exec.Command(worker.path, args...) - - if err := cmd.Run(); err != nil { - return errors.Wrapf(err, "encoding video using: %s", worker.path) + return fmt.Errorf("Executable ffprobe not found: %w", err) } + version, err := exec.Command(path, "-version").Output() + if err != nil { + return fmt.Errorf("Executable ffprobe(%q) not executable: %w", path, err) + } + + log.Println("Found ffprobe:", path, "version:", strings.Split(string(version), "\n")[0]) + ffprobe.SetFFProbeBinPath(path) + return nil } diff --git a/api/scanner/media_encoding/executable_worker/executable_worker_test.go b/api/scanner/media_encoding/executable_worker/executable_worker_test.go index f243f4f6..3cf68c80 100644 --- a/api/scanner/media_encoding/executable_worker/executable_worker_test.go +++ b/api/scanner/media_encoding/executable_worker/executable_worker_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/photoview/photoview/api/scanner/media_encoding/executable_worker" "github.com/photoview/photoview/api/test_utils" ) @@ -43,3 +44,35 @@ func setEnv(key, value string) func() { os.Setenv(key, org) } } + +func TestInitFfprobePath(t *testing.T) { + t.Run("PathFail", func(t *testing.T) { + err := executable_worker.SetFfprobePath() + if err == nil { + t.Fatalf("InitFfprobePath() returns nil, want an error") + } + }) + + t.Run("VersionFail", func(t *testing.T) { + donePath := setPathWithCurrent("./testdata/bin") + defer donePath() + + doneEnv := setEnv("FAIL_WITH", "expect failure") + defer doneEnv() + + err := executable_worker.SetFfprobePath() + if err == nil { + t.Fatalf("InitFfprobePath() returns nil, want an error") + } + }) + + t.Run("Succeed", func(t *testing.T) { + donePath := setPathWithCurrent("./testdata/bin") + defer donePath() + + err := executable_worker.SetFfprobePath() + if err != nil { + t.Fatalf("InitFfprobePath() returns %v, want nil", err) + } + }) +} diff --git a/api/scanner/media_encoding/executable_worker/ffmpeg_cli.go b/api/scanner/media_encoding/executable_worker/ffmpeg_cli.go new file mode 100644 index 00000000..4d38ced6 --- /dev/null +++ b/api/scanner/media_encoding/executable_worker/ffmpeg_cli.go @@ -0,0 +1,108 @@ +package executable_worker + +import ( + "fmt" + "log" + "os/exec" + "strings" + + "github.com/photoview/photoview/api/utils" + "gopkg.in/vansante/go-ffprobe.v2" +) + +const defaultCodec = "h264" + +var hwAccToCodec = map[string]string{ + "qsv": defaultCodec + "_qsv", + "vaapi": defaultCodec + "_vaapi", + "nvenc": defaultCodec + "_nvenc", +} + +type FfmpegCli struct { + path string + videoCodec string +} + +func newFfmpegCli() *FfmpegCli { + if utils.EnvDisableVideoEncoding.GetBool() { + log.Printf("Executable worker disabled (%s=%q): ffmpeg\n", utils.EnvDisableVideoEncoding.GetName(), utils.EnvDisableVideoEncoding.GetValue()) + return nil + } + + path, err := exec.LookPath("ffmpeg") + if err != nil { + log.Println("Executable worker not found: ffmpeg") + return nil + } + + version, err := exec.Command(path, "-version").Output() + if err != nil { + log.Printf("Error getting version of ffmpeg: %s\n", err) + return nil + } + + hwAcc := utils.EnvVideoHardwareAcceleration.GetValue() + codec, ok := hwAccToCodec[hwAcc] + if !ok { + if strings.HasPrefix(hwAcc, "_") { + // A secret way to set the codec directly. + codec = hwAcc[1:] + } else { + codec = defaultCodec + } + } + + log.Printf("Found executable worker: ffmpeg (%s) with codec %q\n", strings.Split(string(version), "\n")[0], codec) + + return &FfmpegCli{ + path: path, + videoCodec: codec, + } +} + +func (worker *FfmpegCli) IsInstalled() bool { + return worker != nil +} + +func (worker *FfmpegCli) EncodeMp4(inputPath string, outputPath string) error { + args := []string{ + "-i", + inputPath, + "-vcodec", worker.videoCodec, + "-acodec", "aac", + "-vf", "scale='min(1080,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2", + "-movflags", "+faststart+use_metadata_tags", + outputPath, + } + + cmd := exec.Command(worker.path, args...) + + if err := cmd.Run(); err != nil { + return fmt.Errorf("encoding video with %q %v error: %w", worker.path, args, err) + } + + return nil +} + +func (worker *FfmpegCli) EncodeVideoThumbnail(inputPath string, outputPath string, probeData *ffprobe.ProbeData) error { + + thumbnailOffsetSeconds := fmt.Sprintf("%.f", probeData.Format.DurationSeconds*0.25) + + args := []string{ + "-ss", thumbnailOffsetSeconds, // grab frame at time offset + "-i", + inputPath, + "-vframes", "1", // output one frame + "-an", // disable audio + "-vf", "scale='min(1024,iw)':'min(1024,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2", + outputPath, + } + + cmd := exec.Command(worker.path, args...) + + if err := cmd.Run(); err != nil { + return fmt.Errorf("encoding video thumbnail with %q %v error: %w", worker.path, args, err) + } + + return nil +} diff --git a/api/scanner/media_encoding/executable_worker/ffmpeg_cli_test.go b/api/scanner/media_encoding/executable_worker/ffmpeg_cli_test.go new file mode 100644 index 00000000..5dcfaa0a --- /dev/null +++ b/api/scanner/media_encoding/executable_worker/ffmpeg_cli_test.go @@ -0,0 +1,133 @@ +package executable_worker_test + +import ( + "regexp" + "testing" + + "github.com/photoview/photoview/api/scanner/media_encoding/executable_worker" + "github.com/photoview/photoview/api/utils" + "gopkg.in/vansante/go-ffprobe.v2" +) + +func TestFfmpegNotExist(t *testing.T) { + done := setPathWithCurrent() + defer done() + + executable_worker.InitializeExecutableWorkers() + + if executable_worker.Ffmpeg.IsInstalled() { + t.Error("Ffmpeg should not be installed, but is found:", executable_worker.Ffmpeg) + } +} + +func TestFfmpegIgnore(t *testing.T) { + donePath := setPathWithCurrent("./testdata/bin") + defer donePath() + + doneEnv := setEnv("PHOTOVIEW_DISABLE_VIDEO_ENCODING", "true") + defer doneEnv() + + executable_worker.InitializeExecutableWorkers() + + if executable_worker.Ffmpeg.IsInstalled() { + t.Error("Ffmpeg should be ignored (as it is disabled), but is initialized:", executable_worker.Ffmpeg) + } +} + +func TestFfmpeg(t *testing.T) { + done := setPathWithCurrent("./testdata/bin") + defer done() + + executable_worker.InitializeExecutableWorkers() + + if !executable_worker.Ffmpeg.IsInstalled() { + t.Error("Ffmpeg should be installed") + } + + t.Run("EncodeMp4Failed", func(t *testing.T) { + doneEnv := setEnv("FAIL_WITH", "expect failure") + defer doneEnv() + + err := executable_worker.Ffmpeg.EncodeMp4("input", "output") + if err == nil { + t.Fatalf("Ffmpeg.EncodeMp4(...) = nil, should be an error.") + } + if got, want := err.Error(), `^encoding video with ".*/testdata/bin/ffmpeg" \[-i input -vcodec h264 .* output\] error: .*$`; !regexp.MustCompile(want).MatchString(got) { + t.Errorf("Ffmpeg.EncodeMp4(...) = %q, should be as reg pattern %q", got, want) + } + }) + + t.Run("EncodeMp4Succeeded", func(t *testing.T) { + err := executable_worker.Ffmpeg.EncodeMp4("input", "output") + if err != nil { + t.Fatalf("Ffmpeg.EncodeMp4(...) = %v, should be nil.", err) + } + }) + + probeData := &ffprobe.ProbeData{ + Format: &ffprobe.Format{ + DurationSeconds: 10, + }, + } + t.Run("EncodeVideoThumbnailMp4Failed", func(t *testing.T) { + doneEnv := setEnv("FAIL_WITH", "expect failure") + defer doneEnv() + + err := executable_worker.Ffmpeg.EncodeVideoThumbnail("input", "output", probeData) + if err == nil { + t.Fatalf("Ffmpeg.EncodeVideoThumbnail(...) = nil, should be an error.") + } + if got, want := err.Error(), `^encoding video thumbnail with ".*/testdata/bin/ffmpeg" \[-ss 2 -i input .* output\] error: .*$`; !regexp.MustCompile(want).MatchString(got) { + t.Errorf("Ffmpeg.EncodeVideoThumbnail(...) = %q, should be as reg pattern %q", got, want) + } + }) + + t.Run("EncodeVideoThumbnailSucceeded", func(t *testing.T) { + err := executable_worker.Ffmpeg.EncodeVideoThumbnail("input", "output", probeData) + if err != nil { + t.Fatalf("Ffmpeg.EncodeVideoThumbnail(...) = %v, should be nil.", err) + } + }) +} + +func TestFfmpegWithHWAcc(t *testing.T) { + doneCodec := setEnv(utils.EnvVideoHardwareAcceleration.GetName(), "qsv") + defer doneCodec() + + donePath := setPathWithCurrent("./testdata/bin") + defer donePath() + + executable_worker.InitializeExecutableWorkers() + + doneEnv := setEnv("FAIL_WITH", "expect failure") + defer doneEnv() + + err := executable_worker.Ffmpeg.EncodeMp4("input", "output") + if err == nil { + t.Fatalf("Ffmpeg.EncodeMp4(...) = nil, should be an error.") + } + if got, want := err.Error(), `^encoding video with ".*/testdata/bin/ffmpeg" \[-i input -vcodec h264_qsv .* output\] error: .*$`; !regexp.MustCompile(want).MatchString(got) { + t.Errorf("Ffmpeg.EncodeMp4(...) = %q, should be as reg pattern %q", got, want) + } +} + +func TestFfmpegWithCustomCOdec(t *testing.T) { + doneCodec := setEnv(utils.EnvVideoHardwareAcceleration.GetName(), "_custom") + defer doneCodec() + + donePath := setPathWithCurrent("./testdata/bin") + defer donePath() + + executable_worker.InitializeExecutableWorkers() + + doneEnv := setEnv("FAIL_WITH", "expect failure") + defer doneEnv() + + err := executable_worker.Ffmpeg.EncodeMp4("input", "output") + if err == nil { + t.Fatalf("Ffmpeg.EncodeMp4(...) = nil, should be an error.") + } + if got, want := err.Error(), `^encoding video with ".*/testdata/bin/ffmpeg" \[-i input -vcodec custom .* output\] error: .*$`; !regexp.MustCompile(want).MatchString(got) { + t.Errorf("Ffmpeg.EncodeMp4(...) = %q, should be as reg pattern %q", got, want) + } +} diff --git a/api/scanner/media_encoding/executable_worker/testdata/bin/ffmpeg b/api/scanner/media_encoding/executable_worker/testdata/bin/ffmpeg new file mode 100755 index 00000000..c98e4784 --- /dev/null +++ b/api/scanner/media_encoding/executable_worker/testdata/bin/ffmpeg @@ -0,0 +1,18 @@ +#!/bin/sh +set -eu + +: ${FAIL_WITH=""} + +case "$1" in + "--version") + echo ffmpeg: version fake + ;; +esac + +if [ "${FAIL_WITH}" != "" ] +then + echo ${FAIL_WITH} + exit -1 +fi + +echo $@ diff --git a/api/scanner/media_encoding/executable_worker/testdata/bin/ffprobe b/api/scanner/media_encoding/executable_worker/testdata/bin/ffprobe new file mode 100755 index 00000000..3a044d2c --- /dev/null +++ b/api/scanner/media_encoding/executable_worker/testdata/bin/ffprobe @@ -0,0 +1,18 @@ +#!/bin/sh +set -eu + +: ${FAIL_WITH=""} + +case "$1" in + "--version") + echo ffprobe: version fake + ;; +esac + +if [ "${FAIL_WITH}" != "" ] +then + echo ${FAIL_WITH} + exit -1 +fi + +echo $@ diff --git a/api/scanner/media_type/media_type.go b/api/scanner/media_type/media_type.go index 83b711b5..8f08d7c0 100644 --- a/api/scanner/media_type/media_type.go +++ b/api/scanner/media_type/media_type.go @@ -264,7 +264,7 @@ func (imgType *MediaType) IsSupported() bool { return true } - if executable_worker.FfmpegCli.IsInstalled() && imgType.IsVideo() { + if executable_worker.Ffmpeg.IsInstalled() && imgType.IsVideo() { return true } diff --git a/api/scanner/scanner_tasks/processing_tasks/process_video_task.go b/api/scanner/scanner_tasks/processing_tasks/process_video_task.go index c042207e..9ce931f1 100644 --- a/api/scanner/scanner_tasks/processing_tasks/process_video_task.go +++ b/api/scanner/scanner_tasks/processing_tasks/process_video_task.go @@ -94,7 +94,7 @@ func (t ProcessVideoTask) ProcessMedia(ctx scanner_task.TaskContext, mediaData * webVideoPath := path.Join(mediaCachePath, webVideoName) - err = executable_worker.FfmpegCli.EncodeMp4(video.Path, webVideoPath) + err = executable_worker.Ffmpeg.EncodeMp4(video.Path, webVideoPath) if err != nil { return []*models.MediaURL{}, errors.Wrapf(err, "could not encode mp4 video (%s)", video.Path) } @@ -139,7 +139,7 @@ func (t ProcessVideoTask) ProcessMedia(ctx scanner_task.TaskContext, mediaData * thumbImagePath := path.Join(mediaCachePath, videoThumbName) - err = executable_worker.FfmpegCli.EncodeVideoThumbnail(video.Path, thumbImagePath, probeData) + err = executable_worker.Ffmpeg.EncodeVideoThumbnail(video.Path, thumbImagePath, probeData) if err != nil { return []*models.MediaURL{}, errors.Wrapf(err, "failed to generate thumbnail for video (%s)", video.Title) } @@ -177,7 +177,7 @@ func (t ProcessVideoTask) ProcessMedia(ctx scanner_task.TaskContext, mediaData * fmt.Printf("Video thumbnail found in database but not in cache, re-encoding photo to cache: %s\n", videoThumbnailURL.MediaName) updatedURLs = append(updatedURLs, videoThumbnailURL) - err = executable_worker.FfmpegCli.EncodeVideoThumbnail(video.Path, thumbImagePath, probeData) + err = executable_worker.Ffmpeg.EncodeVideoThumbnail(video.Path, thumbImagePath, probeData) if err != nil { return []*models.MediaURL{}, errors.Wrapf(err, "failed to generate thumbnail for video (%s)", video.Title) } diff --git a/api/utils/environment_variables.go b/api/utils/environment_variables.go index 76d5861a..b388fc3d 100644 --- a/api/utils/environment_variables.go +++ b/api/utils/environment_variables.go @@ -35,9 +35,10 @@ const ( // Feature related const ( - EnvDisableFaceRecognition EnvironmentVariable = "PHOTOVIEW_DISABLE_FACE_RECOGNITION" - EnvDisableVideoEncoding EnvironmentVariable = "PHOTOVIEW_DISABLE_VIDEO_ENCODING" - EnvDisableRawProcessing EnvironmentVariable = "PHOTOVIEW_DISABLE_RAW_PROCESSING" + EnvDisableFaceRecognition EnvironmentVariable = "PHOTOVIEW_DISABLE_FACE_RECOGNITION" + EnvDisableVideoEncoding EnvironmentVariable = "PHOTOVIEW_DISABLE_VIDEO_ENCODING" + EnvDisableRawProcessing EnvironmentVariable = "PHOTOVIEW_DISABLE_RAW_PROCESSING" + EnvVideoHardwareAcceleration EnvironmentVariable = "PHOTOVIEW_VIDEO_HARDWARE_ACCELERATION" ) // GetName returns the name of the environment variable itself diff --git a/dev-compose.yaml b/dev-compose.yaml index 10861f44..b0232aab 100644 --- a/dev-compose.yaml +++ b/dev-compose.yaml @@ -1,7 +1,7 @@ name: photoview services: - dev-ui: + ui: image: photoview/ui build: context: . @@ -21,7 +21,7 @@ services: npm ci npm run mon - dev-api: + api: image: photoview/api build: context: . diff --git a/docker-compose example/docker-compose.example.yml b/docker-compose example/docker-compose.example.yml index bf32804b..a2b4de15 100644 --- a/docker-compose example/docker-compose.example.yml +++ b/docker-compose example/docker-compose.example.yml @@ -42,6 +42,10 @@ services: ## A token can be generated for free here https://account.mapbox.com/access-tokens/ ## It's a good idea to limit the scope of the token to your own domain, to prevent others from using it. MAPBOX_TOKEN: ${MAPBOX_TOKEN} + ## If you want to use it, set the correct value in the .env file. + ## Support `qsv`, `vaapi`, `nvenc`. + ## Only `qsv` is verified with `/dev/dri` devices (see below `devices`). + PHOTOVIEW_VIDEO_HARDWARE_ACCELERATION: ${PHOTOVIEW_VIDEO_HARDWARE_ACCELERATION} ## Share hardware devices with FFmpeg (optional): # devices: ## Uncomment next devices mappings if they are available in your host system diff --git a/docker-compose example/example.env b/docker-compose example/example.env index 4a41c941..d4fae280 100644 --- a/docker-compose example/example.env +++ b/docker-compose example/example.env @@ -31,6 +31,13 @@ PHOTOVIEW_DATABASE_DRIVER=mysql # MAPBOX_TOKEN=yourToken ##-----------------------------------## +##----------Video variables----------## +## Set the hardware acceleration when encoding videos. +## Support `qsv`, `vaapi`, `nvenc`. +## Only `qsv` is verified with `/dev/dri` devices. +# PHOTOVIEW_VIDEO_HARDWARE_ACCELERATION= +##-----------------------------------## + ##--------MariaDB variables----------## ## Comment out these variables if PHOTOVIEW_DATABASE_DRIVER is `sqlite` or `postgres` ## Use password generator to generate secret values and replace these defaults