mirror of
https://github.com/asticode/go-astiav.git
synced 2025-10-06 00:26:52 +08:00

* Adding HW Context * Check for hw pixel format and transfer data on ReciveFrame when its hw pixel format * Remove hw_decoding example and update demuxing_dec example * unref hw_device_ctx, and improve example Unref correctly hw_device_ctx on free decoding example has now a flag for hw decoding Use error on hw context because its good to know the error that happen Add a method to list supported hw codecs so user could check if system has a specif hwcodec before the create a hw context * Fix test to use err, and update hw with device to also return error * revert changes in demux example * Add/Modify according to review Seperate Hardware Device Context and Test Renaming HW to Hardware Create dedicated hardware_decoding Example Update Readme Add TransferHardwareData to frame.go Add SetHardwareDeviceContext to codec_context.go * Review changes add HardwareConfigs to codec create codec_hardware_config create codec_hardware_config_method_flag update example update flags better error handling in TransferHardwareData update CreateHardwareDeviceContext, better null handling remove hw ctx test because it fails when no nvidia is present some reordering of conss in hardware_device_type * Minor tweaks (review changes) HardwareConfigs only returns a slice an no error anymore remove if HardwareDeviceType checking in codec.go this should be done by user order codec hw_config_method_flags constants alphabetically some small changes in example ## hardware_device_context.go change link update optionsC
194 lines
5.2 KiB
Go
194 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/asticode/go-astiav"
|
|
)
|
|
|
|
var (
|
|
hardwareDeviceTypeName = flag.String("d", "", "the hardware device type like: cuda")
|
|
input = flag.String("i", "", "the input path")
|
|
)
|
|
|
|
type stream struct {
|
|
decCodec *astiav.Codec
|
|
decCodecContext *astiav.CodecContext
|
|
hardwareDeviceContext *astiav.HardwareDeviceContext
|
|
hardwarePixelFormat astiav.PixelFormat
|
|
inputStream *astiav.Stream
|
|
}
|
|
|
|
func main() {
|
|
// Handle ffmpeg logs
|
|
astiav.SetLogLevel(astiav.LogLevelDebug)
|
|
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
|
|
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
|
|
})
|
|
|
|
// Parse flags
|
|
flag.Parse()
|
|
|
|
// Usage
|
|
if *input == "" || *hardwareDeviceTypeName == "" {
|
|
log.Println("Usage: <binary path> -d <device type> -i <input path>")
|
|
return
|
|
}
|
|
|
|
// Get hardware device type
|
|
hardwareDeviceType := astiav.FindHardwareDeviceTypeByName(*hardwareDeviceTypeName)
|
|
if hardwareDeviceType == astiav.HardwareDeviceTypeNone {
|
|
log.Fatal(errors.New("main: hardware device not found"))
|
|
}
|
|
|
|
// Alloc packet
|
|
pkt := astiav.AllocPacket()
|
|
defer pkt.Free()
|
|
|
|
// Alloc hardware frame
|
|
hardwareFrame := astiav.AllocFrame()
|
|
defer hardwareFrame.Free()
|
|
|
|
// Alloc software frame
|
|
softwareFrame := astiav.AllocFrame()
|
|
defer softwareFrame.Free()
|
|
|
|
// Alloc input format context
|
|
inputFormatContext := astiav.AllocFormatContext()
|
|
if inputFormatContext == nil {
|
|
log.Fatal(errors.New("main: input format context is nil"))
|
|
}
|
|
defer inputFormatContext.Free()
|
|
|
|
// Open input
|
|
if err := inputFormatContext.OpenInput(*input, nil, nil); err != nil {
|
|
log.Fatal(fmt.Errorf("main: opening input failed: %w", err))
|
|
}
|
|
defer inputFormatContext.CloseInput()
|
|
|
|
// Find stream info
|
|
if err := inputFormatContext.FindStreamInfo(nil); err != nil {
|
|
log.Fatal(fmt.Errorf("main: finding stream info failed: %w", err))
|
|
}
|
|
|
|
// Loop through streams
|
|
streams := make(map[int]*stream) // Indexed by input stream index
|
|
for _, is := range inputFormatContext.Streams() {
|
|
var err error
|
|
|
|
// Only process video
|
|
if is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
|
|
continue
|
|
}
|
|
|
|
// Create stream
|
|
s := &stream{inputStream: is}
|
|
|
|
// Find decoder
|
|
if s.decCodec = astiav.FindDecoder(is.CodecParameters().CodecID()); s.decCodec == nil {
|
|
log.Fatal(errors.New("main: codec is nil"))
|
|
}
|
|
|
|
// Alloc codec context
|
|
if s.decCodecContext = astiav.AllocCodecContext(s.decCodec); s.decCodecContext == nil {
|
|
log.Fatal(errors.New("main: codec context is nil"))
|
|
}
|
|
defer s.decCodecContext.Free()
|
|
|
|
// Get codec hardware configs
|
|
hardwareConfigs := s.decCodec.HardwareConfigs(hardwareDeviceType)
|
|
|
|
// Loop through codec hardware configs
|
|
for _, p := range hardwareConfigs {
|
|
// Valid hardware config
|
|
if p.MethodFlags().Has(astiav.CodecHardwareConfigMethodHwDeviceCtx) && p.HardwareDeviceType() == hardwareDeviceType {
|
|
s.hardwarePixelFormat = p.PixelFormat()
|
|
break
|
|
}
|
|
}
|
|
|
|
// No valid hardware pixel format
|
|
if s.hardwarePixelFormat == astiav.PixelFormatNone {
|
|
log.Fatal(errors.New("main: hardware device type not supported by decoder"))
|
|
}
|
|
|
|
// Update codec context
|
|
if err := is.CodecParameters().ToCodecContext(s.decCodecContext); err != nil {
|
|
log.Fatal(fmt.Errorf("main: updating codec context failed: %w", err))
|
|
}
|
|
|
|
// Create hardware device context
|
|
s.hardwareDeviceContext, err = astiav.CreateHardwareDeviceContext(hardwareDeviceType, "", nil)
|
|
if err != nil {
|
|
log.Fatal(fmt.Errorf("main: creating hardware device context failed: %w", err))
|
|
}
|
|
s.decCodecContext.SetHardwareDeviceContext(s.hardwareDeviceContext)
|
|
|
|
// Open codec context
|
|
if err := s.decCodecContext.Open(s.decCodec, nil); err != nil {
|
|
log.Fatal(fmt.Errorf("main: opening codec context failed: %w", err))
|
|
}
|
|
|
|
// Add stream
|
|
streams[is.Index()] = s
|
|
}
|
|
|
|
// Loop through packets
|
|
for {
|
|
// Read frame
|
|
if err := inputFormatContext.ReadFrame(pkt); err != nil {
|
|
if errors.Is(err, astiav.ErrEof) {
|
|
break
|
|
}
|
|
log.Fatal(fmt.Errorf("main: reading frame failed: %w", err))
|
|
}
|
|
|
|
// Get stream
|
|
s, ok := streams[pkt.StreamIndex()]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// Send packet
|
|
if err := s.decCodecContext.SendPacket(pkt); err != nil {
|
|
log.Fatal(fmt.Errorf("main: sending packet failed: %w", err))
|
|
}
|
|
|
|
// Loop
|
|
for {
|
|
// Receive frame
|
|
if err := s.decCodecContext.ReceiveFrame(hardwareFrame); err != nil {
|
|
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
|
|
break
|
|
}
|
|
log.Fatal(fmt.Errorf("main: receiving frame failed: %w", err))
|
|
}
|
|
|
|
// Get final frame
|
|
var finalFrame *astiav.Frame
|
|
if hardwareFrame.PixelFormat() == s.hardwarePixelFormat {
|
|
// Transfer hardware data
|
|
if err := hardwareFrame.TransferHardwareData(softwareFrame); err != nil {
|
|
log.Fatal(fmt.Errorf("main: transferring hardware data failed: %w", err))
|
|
}
|
|
|
|
// Update pts
|
|
softwareFrame.SetPts(hardwareFrame.Pts())
|
|
|
|
// Update final frame
|
|
finalFrame = softwareFrame
|
|
} else {
|
|
// Update final frame
|
|
finalFrame = hardwareFrame
|
|
}
|
|
|
|
// Do something with decoded frame
|
|
log.Printf("new frame: stream %d - pts: %d", pkt.StreamIndex(), finalFrame.Pts())
|
|
}
|
|
}
|
|
}
|