implement libav in some methods
This commit is contained in:
parent
9489689dbf
commit
d92f02d38e
7 changed files with 259 additions and 56 deletions
|
@ -1,24 +1,115 @@
|
|||
package av
|
||||
|
||||
import (
|
||||
ffmpeg "github.com/u2takey/ffmpeg-go"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/jpeg"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/asticode/go-astiav"
|
||||
)
|
||||
|
||||
func ExtractVideoThumbnail(
|
||||
videoPath string,
|
||||
thumbnailPath string,
|
||||
) error {
|
||||
err := ffmpeg.
|
||||
Input(videoPath).
|
||||
Output(thumbnailPath, ffmpeg.KwArgs{
|
||||
"vframes": 1,
|
||||
"ss": "00:00:01",
|
||||
}).
|
||||
Silent(true).
|
||||
OverWriteOutput().
|
||||
Run()
|
||||
func ExtractVideoThumbnail(videoPath string, imagePath string) error {
|
||||
formatCtx := astiav.AllocFormatContext()
|
||||
defer formatCtx.Free()
|
||||
|
||||
err := formatCtx.OpenInput(videoPath, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed opening input: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
err = formatCtx.FindStreamInfo(nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed finding stream info: %w", err)
|
||||
}
|
||||
|
||||
stream, codec, err := formatCtx.FindBestStream(
|
||||
astiav.MediaTypeVideo, -1, -1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed finding best video stream: %w", err)
|
||||
}
|
||||
|
||||
codecParameters := stream.CodecParameters()
|
||||
|
||||
decoder := astiav.FindDecoder(codec.ID())
|
||||
if decoder == nil {
|
||||
return fmt.Errorf("no decoder found for codec %s", codec.String())
|
||||
}
|
||||
|
||||
codecCtx := astiav.AllocCodecContext(decoder)
|
||||
defer codecCtx.Free()
|
||||
|
||||
err = codecParameters.ToCodecContext(codecCtx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting codec parameters: %w", err)
|
||||
}
|
||||
|
||||
err = codecCtx.Open(decoder, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed opening codec: %w", err)
|
||||
}
|
||||
|
||||
packet := astiav.AllocPacket()
|
||||
defer packet.Free()
|
||||
frame := astiav.AllocFrame()
|
||||
defer frame.Free()
|
||||
|
||||
startTime := time.Now()
|
||||
timeout := 5 * time.Second
|
||||
|
||||
// read frames until we find a video frame or timeout
|
||||
for time.Since(startTime) < timeout {
|
||||
err := formatCtx.ReadFrame(packet)
|
||||
if err != nil {
|
||||
if errors.Is(err, astiav.ErrEof) {
|
||||
return fmt.Errorf("end of file reached before finding video frame")
|
||||
}
|
||||
return fmt.Errorf("failed reading frame: %w", err)
|
||||
}
|
||||
|
||||
if packet.StreamIndex() != stream.Index() {
|
||||
packet.Unref()
|
||||
continue
|
||||
}
|
||||
|
||||
err = codecCtx.SendPacket(packet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed sending packet to decoder: %w", err)
|
||||
}
|
||||
|
||||
err = codecCtx.ReceiveFrame(frame)
|
||||
if err != nil {
|
||||
if errors.Is(err, astiav.ErrEagain) || errors.Is(err, astiav.ErrEof) {
|
||||
packet.Unref()
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("failed receiving frame from decoder: %w", err)
|
||||
}
|
||||
|
||||
img, err := frame.Data().GuessImageFormat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed guessing image format: %w", err)
|
||||
}
|
||||
err = frame.Data().ToImage(img)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed converting frame to image: %w", err)
|
||||
}
|
||||
packet.Unref()
|
||||
|
||||
file, err := os.Create(imagePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed creating image file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = jpeg.Encode(file, img, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed encoding image: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("timeout while waiting for video frame")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue