diff --git a/examples/hardware_decoding_filtering/main.go b/examples/hardware_decoding_filtering/main.go index 435731e..0effaf7 100644 --- a/examples/hardware_decoding_filtering/main.go +++ b/examples/hardware_decoding_filtering/main.go @@ -102,14 +102,16 @@ func main() { continue } + // Merge decoder name with hardware device type name + if *decoderCodecName == "" { + *decoderCodecName = fmt.Sprintf("%s_%s", is.CodecParameters().CodecID().Name(), *hardwareDeviceTypeName) + } + // Update input stream inputStream = is // Find decoder - decCodec = astiav.FindDecoder(is.CodecParameters().CodecID()) - if *decoderCodecName != "" { - decCodec = astiav.FindDecoderByName(*decoderCodecName) - } + decCodec = astiav.FindDecoderByName(*decoderCodecName) // No codec if decCodec == nil { @@ -122,20 +124,6 @@ func main() { } c.Add(decCodecContext.Free) - // Loop through codec hardware configs - for _, p := range decCodec.HardwareConfigs() { - // Valid hardware config - if p.MethodFlags().Has(astiav.CodecHardwareConfigMethodFlagHwDeviceCtx) && p.HardwareDeviceType() == hardwareDeviceType { - hardwarePixelFormat = p.PixelFormat() - break - } - } - - // No valid hardware pixel format - if hardwarePixelFormat == astiav.PixelFormatNone { - log.Fatal(errors.New("main: hardware device type not supported by decoder")) - } - // Update codec context if err := is.CodecParameters().ToCodecContext(decCodecContext); err != nil { log.Fatal(fmt.Errorf("main: updating codec context failed: %w", err)) @@ -148,6 +136,20 @@ func main() { } c.Add(hardwareDeviceContext.Free) + hardwareFramesConstraints := hardwareDeviceContext.HardwareFramesConstraints() + if hardwareFramesConstraints == nil { + log.Fatal("main: hardware frames constraints is nil") + return + } + defer hardwareFramesConstraints.Free() + + validHardwarePixelFormats := hardwareFramesConstraints.ValidHardwarePixelFormats() + if len(validHardwarePixelFormats) == 0 { + log.Fatal("main: no valid hardware pixel formats") + return + } + hardwarePixelFormat = validHardwarePixelFormats[0] + // Update decoder context decCodecContext.SetHardwareDeviceContext(hardwareDeviceContext) decCodecContext.SetPixelFormatCallback(func(pfs []astiav.PixelFormat) astiav.PixelFormat { @@ -160,9 +162,9 @@ func main() { return astiav.PixelFormatNone }) - // Open codec context + // Open decoder context if err := decCodecContext.Open(decCodec, nil); err != nil { - log.Fatal(fmt.Errorf("main: opening codec context failed: %w", err)) + log.Fatal(fmt.Errorf("main: opening decoder context failed: %w", err)) } break } diff --git a/examples/hardware_encoding/main.go b/examples/hardware_encoding/main.go index 64080b1..eee67cf 100644 --- a/examples/hardware_encoding/main.go +++ b/examples/hardware_encoding/main.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "log" + "slices" "strings" "github.com/asticode/go-astiav" @@ -55,6 +56,19 @@ func main() { } defer hardwareDeviceContext.Free() + hardwareFramesConstraints := hardwareDeviceContext.HardwareFramesConstraints() + if hardwareFramesConstraints == nil { + log.Fatal("main: hardware frames constraints is nil") + return + } + defer hardwareFramesConstraints.Free() + + validHardwarePixelFormats := hardwareFramesConstraints.ValidHardwarePixelFormats() + if len(validHardwarePixelFormats) == 0 { + log.Fatal("main: no valid hardware pixel formats") + return + } + // Find encoder codec encCodec := astiav.FindEncoderByName(*encoderCodecName) if encCodec == nil { @@ -72,6 +86,8 @@ func main() { hardwarePixelFormat := astiav.FindPixelFormatByName(*hardwarePixelFormatName) if hardwarePixelFormat == astiav.PixelFormatNone { log.Fatal("main: hardware pixel format not found") + } else if !slices.Contains(validHardwarePixelFormats, hardwarePixelFormat) { + log.Fatalf("main: hardware pixel format not supported : %s", hardwarePixelFormat) } // Set codec context @@ -88,10 +104,18 @@ func main() { } defer hardwareFramesContext.Free() + validSoftwarePixelFormats := hardwareFramesConstraints.ValidSoftwarePixelFormats() + if len(validSoftwarePixelFormats) == 0 { + log.Fatal("main: no valid software pixel formats") + return + } + // Get software pixel format softwarePixelFormat := astiav.FindPixelFormatByName(*softwarePixelFormatName) if softwarePixelFormat == astiav.PixelFormatNone { log.Fatal("main: software pixel format not found") + } else if !slices.Contains(validSoftwarePixelFormats, softwarePixelFormat) { + log.Fatalf("main: software pixel format not supported : %s", softwarePixelFormat) } // Set hardware frame content diff --git a/hardware_device_context.go b/hardware_device_context.go index b8bcb01..e7dbb4f 100644 --- a/hardware_device_context.go +++ b/hardware_device_context.go @@ -30,6 +30,11 @@ func CreateHardwareDeviceContext(t HardwareDeviceType, device string, options *D return &hdc, nil } +// https://ffmpeg.org/doxygen/7.0/hwcontext_8c.html#a80f4c1184e1758150b6d9bc0adf2c1df +func (hdc *HardwareDeviceContext) HardwareFramesConstraints() *HardwareFramesConstraints { + return newHardwareFramesConstraintsFromC(C.av_hwdevice_get_hwframe_constraints(hdc.c, nil)) +} + func (hdc *HardwareDeviceContext) Free() { if hdc.c != nil { C.av_buffer_unref(&hdc.c) diff --git a/hardware_frames_constraints.go b/hardware_frames_constraints.go new file mode 100644 index 0000000..7d3eb5d --- /dev/null +++ b/hardware_frames_constraints.go @@ -0,0 +1,70 @@ +package astiav + +//#include +import "C" +import "unsafe" + +// https://ffmpeg.org/doxygen/7.0/structAVHWFramesConstraints.html +type HardwareFramesConstraints struct { + c *C.AVHWFramesConstraints +} + +func newHardwareFramesConstraintsFromC(c *C.AVHWFramesConstraints) *HardwareFramesConstraints { + if c == nil { + return nil + } + return &HardwareFramesConstraints{c: c} +} + +func (hfc *HardwareFramesConstraints) pixelFormats(formats *C.enum_AVPixelFormat) (o []PixelFormat) { + if formats == nil { + return nil + } + size := unsafe.Sizeof(*formats) + for i := 0; ; i++ { + p := *(*C.int)(unsafe.Pointer(uintptr(unsafe.Pointer(formats)) + uintptr(i)*size)) + if p == C.AV_PIX_FMT_NONE { + break + } + o = append(o, PixelFormat(p)) + } + return +} + +// https://ffmpeg.org/doxygen/7.0/structAVHWFramesConstraints.html#a4258bbe81f927b76b7ca5af44ba7ef6b +func (hfc *HardwareFramesConstraints) ValidHardwarePixelFormats() (o []PixelFormat) { + return hfc.pixelFormats(hfc.c.valid_hw_formats) +} + +// https://ffmpeg.org/doxygen/7.0/structAVHWFramesConstraints.html#aabea88093c6f85d6185ffb0852a2217f +func (hfc *HardwareFramesConstraints) ValidSoftwarePixelFormats() (o []PixelFormat) { + return hfc.pixelFormats(hfc.c.valid_sw_formats) +} + +// https://ffmpeg.org/doxygen/7.0/structAVHWFramesConstraints.html#af220776925452091085139081d5d7251 +func (hfc *HardwareFramesConstraints) MinWidth() int { + return int(hfc.c.min_width) +} + +// https://ffmpeg.org/doxygen/7.0/structAVHWFramesConstraints.html#a3f1aec6d1c90f77837875c2a3598be46 +func (hfc *HardwareFramesConstraints) MinHeight() int { + return int(hfc.c.min_height) +} + +// https://ffmpeg.org/doxygen/7.0/structAVHWFramesConstraints.html#a34e06e3397af2b83de9d78f893bf4168 +func (hfc *HardwareFramesConstraints) MaxWidth() int { + return int(hfc.c.max_width) +} + +// https://ffmpeg.org/doxygen/7.0/structAVHWFramesConstraints.html#af5d3a683727f7b92abca7b7114d4e15c +func (hfc *HardwareFramesConstraints) MaxHeight() int { + return int(hfc.c.max_height) +} + +// https://ffmpeg.org/doxygen/7.0/hwcontext_8c.html#a29da7fa7ffa73266d1cbfccb116ed634 +func (hfc *HardwareFramesConstraints) Free() { + if hfc.c != nil { + C.av_hwframe_constraints_free(&hfc.c) + hfc.c = nil + } +}