Files
alfred-be/alfred/pkg/ffmpeg/media.go
2026-03-08 16:14:42 +05:30

253 lines
6.7 KiB
Go

package ffmpeg
import (
"alfred/config"
"alfred/pkg/log"
"alfred/utils"
"encoding/json"
"errors"
"fmt"
"github.com/u2takey/ffmpeg-go"
"go.uber.org/zap"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/fixed"
"golang.org/x/image/webp"
"image"
"image/color"
"image/draw"
"image/jpeg"
"os"
"os/exec"
"path/filepath"
"strconv"
)
func GenerateVideoFromImages(folderPath, filename, imageType string, snapshotPerSecond int64) (string, error) {
err := ffmpeg_go.
Input(
filepath.Join(folderPath, utils.ASTERISK+imageType),
ffmpeg_go.KwArgs{"framerate": snapshotPerSecond},
ffmpeg_go.KwArgs{"pattern_type": "glob"},
).
Filter("scale", ffmpeg_go.Args{"trunc(iw/2)*2:trunc(ih/2)*2"}).
Output(
filepath.Join(folderPath, filename+utils.VideoExtension.String()),
ffmpeg_go.KwArgs{"codec": "libx264"},
ffmpeg_go.KwArgs{"crf": 20},
ffmpeg_go.KwArgs{"pix_fmt": "yuv420p"},
ffmpeg_go.KwArgs{"preset": "ultrafast"},
).
OverWriteOutput().Run()
if err != nil {
return utils.EMPTY, err
}
return filepath.Join(folderPath, filename, filename+utils.VideoExtension.String()), nil
}
func ApplyBlurToRegion(inputImage, destFile string, height, width int, blurRatio, blurStrength string) error {
ratio, err := strconv.ParseFloat(blurRatio, 64)
if err != nil {
return err
}
cropHeight := int(float64(height) / ratio)
x := 0
y := cropHeight
cropWidth := width
inputVideo := ffmpeg_go.Input(inputImage)
croppedStream := inputVideo.Filter("crop", ffmpeg_go.Args{fmt.Sprintf("%d:%d:%d:%d", cropWidth, cropHeight, x, height-y)})
finalStream := croppedStream.Filter("avgblur", ffmpeg_go.Args{blurStrength})
err = ffmpeg_go.Input(inputImage).Overlay(finalStream, fmt.Sprintf("%d:%d", x, height-y)).Output(destFile).OverWriteOutput().Run()
if err != nil {
return err
}
return nil
}
func ApplyTouchPoints(inputImageFile, outputImageFile string, x, y int, tempDirPath, imageType string) error {
imgFile, err := os.Open(inputImageFile)
if err != nil {
log.Error("Error while opening image", zap.String("inputImage", inputImageFile), zap.Error(err))
return err
}
defer imgFile.Close()
var img image.Image
switch imageType {
case utils.ImageExtensionWebp.String():
img, err = webp.Decode(imgFile)
case utils.ImageExtensionJpeg.String():
img, _, err = image.Decode(imgFile)
}
if err != nil {
log.Error("Error while decoding image", zap.String("inputImage", inputImageFile), zap.String("image_type", imageType), zap.Error(err))
return err
}
// Create a new RGBA image to draw on.
rgba := image.NewRGBA(img.Bounds())
draw.Draw(rgba, img.Bounds(), img, image.Point{}, draw.Src)
circleRadius := config.GetCoreConfig().TouchPointsConfig.TouchPointCircleRadius
circleColor := color.RGBA{R: uint8(config.GetCoreConfig().TouchPointsConfig.TouchPointRedComponent), G: uint8(config.GetCoreConfig().TouchPointsConfig.TouchPointGreenComponent), B: uint8(config.GetCoreConfig().TouchPointsConfig.TouchPointBlueComponent), A: uint8(config.GetCoreConfig().TouchPointsConfig.TouchPointAlphaComponent)}
for px := x - circleRadius; px <= x+circleRadius; px++ {
for py := y - circleRadius; py <= y+circleRadius; py++ {
if (px-x)*(px-x)+(py-y)*(py-y) <= circleRadius*circleRadius {
rgba.Set(px, py, circleColor)
}
}
}
err = saveImage(rgba, outputImageFile, tempDirPath)
if err != nil {
return err
}
return nil
}
func GenerateThirdPartyImage(imageDest, screen, pathToUnzippedFiles string, height, width int, imageType string) error {
tempDir := utils.ThirdPartyDirectory
tempDirPath := filepath.Join(pathToUnzippedFiles, tempDir)
validFileName := utils.ConvertToValidFilename(screen)
tempFile := filepath.Join(tempDirPath, validFileName+imageType)
if utils.FolderExists(tempDirPath) && utils.FolderExists(tempFile) {
err := utils.CopyFile(tempFile, imageDest)
if err != nil {
return err
}
return nil
}
if !utils.FolderExists(tempDirPath) {
_ = utils.CreateDirectory(tempDirPath)
}
// Create a new white image
img := image.NewRGBA(image.Rect(0, 0, width, height))
draw.Draw(img, img.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
// Use basicfont
face := basicfont.Face7x13
textWidth := font.MeasureString(face, "Third Party Screen "+screen).Ceil()
// Calculate text position at the center
x := 1
if textWidth > width {
x += (textWidth - width) / 2
} else {
x += (width - textWidth) / 2
}
y := (height) / 2
// Draw the text on the image
drawer := &font.Drawer{
Dst: img,
Src: image.Black,
Face: face,
Dot: fixed.P(x, y),
}
drawer.DrawString("Third Party Screen " + screen)
// Save the image to a file (overwriting if it already exists)
err := saveImage(img, tempFile, tempDirPath)
if err != nil {
return err
}
err = utils.CopyFile(tempFile, imageDest)
if err != nil {
return err
}
return nil
}
func ExtractImageDimensions(inputImage string) (int, int, error) {
inputInfo, err := ffmpeg_go.Probe(inputImage)
if err != nil {
return 0, 0, err
}
var probeInfo struct {
Streams []struct {
Width int `json:"width"`
Height int `json:"height"`
} `json:"streams"`
}
if err := json.Unmarshal([]byte(inputInfo), &probeInfo); err != nil {
log.Error("Error unmarshaling JSON data: %v", zap.Error(err))
}
if len(probeInfo.Streams) <= 0 {
return 0, 0, err
}
width := probeInfo.Streams[0].Width
height := probeInfo.Streams[0].Height
return height, width, nil
}
func saveImage(img *image.RGBA, filename, tempDirPath string) error {
outputFile, err := os.Create(filename)
if err != nil {
log.Error("Error while creating output image file", zap.String("inputImage", filename), zap.String("outputImage", filename), zap.Error(err))
return err
}
defer outputFile.Close()
if filepath.Ext(filename) == utils.ImageExtensionJpeg.String() {
err = jpeg.Encode(outputFile, img, nil)
} else if filepath.Ext(filename) == utils.ImageExtensionWebp.String() {
err = encodeToWebp(img, filename, tempDirPath)
} else {
log.Error("Unsupported image format", zap.String("inputImage", filename), zap.String("outputImage", filename), zap.Error(err))
return errors.New("unsupported image format")
}
// Encode the modified image to a JPEG file.
if err != nil {
log.Error("Error while encoding image", zap.String("inputImage", filename), zap.String("outputImage", filename), zap.Error(err))
return err
}
return nil
}
func encodeToWebp(img image.Image, filename, tempDirPath string) error {
tmpFile := filepath.Join(tempDirPath, "tempImage.jpeg")
outFile, err := os.Create(tmpFile)
if err != nil {
return err
}
defer outFile.Close()
err = jpeg.Encode(outFile, img, nil)
if err != nil {
return err
}
cmd := exec.Command("cwebp", tmpFile, "-o", filename)
err = cmd.Run()
if err != nil {
return err
}
os.Remove(tmpFile)
return nil
}