From 6c2fa5975530657003c4c9918e2dbcfb96df06f5 Mon Sep 17 00:00:00 2001 From: Wu Wenxuan Date: Wed, 4 Sep 2024 13:43:19 +0800 Subject: [PATCH 1/5] feat: add hardware frame context --- codec_context.go | 16 ++++++++++++- frame.go | 4 ++++ hardware_frame_context.go | 48 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 hardware_frame_context.go diff --git a/codec_context.go b/codec_context.go index 5ea3044..e819596 100644 --- a/codec_context.go +++ b/codec_context.go @@ -37,6 +37,7 @@ type CodecContext struct { c *C.struct_AVCodecContext // We need to store this to unref it properly hdc *HardwareDeviceContext + hfc *HardwareFrameContext } func newCodecContextFromC(c *C.struct_AVCodecContext) *CodecContext { @@ -63,6 +64,10 @@ func (cc *CodecContext) Free() { C.av_buffer_unref(&cc.hdc.c) cc.hdc = nil } + if cc.hfc != nil { + C.av_buffer_unref(&cc.hfc.c) + cc.hfc = nil + } classers.del(cc) C.avcodec_free_context(&cc.c) } @@ -341,6 +346,16 @@ func (cc *CodecContext) SetHardwareDeviceContext(hdc *HardwareDeviceContext) { } } +func (cc *CodecContext) SetHardwareFrameContext(hfc *HardwareFrameContext) { + if cc.c.hw_frames_ctx != nil { + C.av_buffer_unref(&cc.c.hw_frames_ctx) + } + cc.hfc = hfc + if cc.hfc != nil { + cc.c.hw_frames_ctx = C.av_buffer_ref(cc.hfc.c) + } +} + func (cc *CodecContext) ExtraHardwareFrames() int { return int(cc.c.extra_hw_frames) } @@ -391,5 +406,4 @@ func goAstiavCodecContextGetFormat(cc *C.struct_AVCodecContext, pfsCPtr *C.enum_ // Callback return C.enum_AVPixelFormat(c(pfs)) - } diff --git a/frame.go b/frame.go index 685f79f..252a027 100644 --- a/frame.go +++ b/frame.go @@ -33,6 +33,10 @@ func (f *Frame) AllocBuffer(align int) error { return newError(C.av_frame_get_buffer(f.c, C.int(align))) } +func (f *Frame) AllocHWBuffer(hfc *HardwareFrameContext, align int) error { + return newError(C.av_hwframe_get_buffer(hfc.c, f.c, C.int(align))) +} + func (f *Frame) AllocImage(align int) error { return newError(C.av_image_alloc(&f.c.data[0], &f.c.linesize[0], f.c.width, f.c.height, (C.enum_AVPixelFormat)(f.c.format), C.int(align))) } diff --git a/hardware_frame_context.go b/hardware_frame_context.go new file mode 100644 index 0000000..39bbe2c --- /dev/null +++ b/hardware_frame_context.go @@ -0,0 +1,48 @@ +package astiav + +//#include +import "C" +import ( + "fmt" + "unsafe" +) + +type HardwareFrameContext struct { + c *C.struct_AVBufferRef +} + +func AllocHardwareFrameContext(hdc *HardwareDeviceContext) (*HardwareFrameContext, error) { + if hfc := C.av_hwframe_ctx_alloc(hdc.c); hfc != nil { + return &HardwareFrameContext{c: hfc}, nil + } + return nil, fmt.Errorf("failed to allocate hardware frame context") +} + +func (hfc *HardwareFrameContext) SetWidth(width int) { + frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) + frameCtx.width = C.int(width) +} + +func (hfc *HardwareFrameContext) SetHeight(height int) { + frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) + frameCtx.height = C.int(height) +} + +func (hfc *HardwareFrameContext) SetFormat(format PixelFormat) { + frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) + frameCtx.format = C.enum_AVPixelFormat(format) +} + +func (hfc *HardwareFrameContext) SetSWFormat(swFormat PixelFormat) { + frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) + frameCtx.sw_format = C.enum_AVPixelFormat(swFormat) +} + +func (hfc *HardwareFrameContext) SetInitialPoolSize(initialPoolSize int) { + frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) + frameCtx.initial_pool_size = C.int(initialPoolSize) +} + +func (hfc *HardwareFrameContext) Init() error { + return newError(C.av_hwframe_ctx_init(hfc.c)) +} From 84fc4e4dba6bfda042528ea0b8c025f1f18fd0b3 Mon Sep 17 00:00:00 2001 From: Wu Wenxuan Date: Thu, 5 Sep 2024 10:47:08 +0800 Subject: [PATCH 2/5] fix: pr comments --- codec_context.go | 4 ++-- frame.go | 4 ++-- hardware_frame_context.go | 39 +++++++++++++++++++++------------------ 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/codec_context.go b/codec_context.go index e819596..714fbf0 100644 --- a/codec_context.go +++ b/codec_context.go @@ -347,8 +347,8 @@ func (cc *CodecContext) SetHardwareDeviceContext(hdc *HardwareDeviceContext) { } func (cc *CodecContext) SetHardwareFrameContext(hfc *HardwareFrameContext) { - if cc.c.hw_frames_ctx != nil { - C.av_buffer_unref(&cc.c.hw_frames_ctx) + if cc.hfc != nil { + C.av_buffer_unref(&cc.hfc.c) } cc.hfc = hfc if cc.hfc != nil { diff --git a/frame.go b/frame.go index 252a027..b7adbdc 100644 --- a/frame.go +++ b/frame.go @@ -33,8 +33,8 @@ func (f *Frame) AllocBuffer(align int) error { return newError(C.av_frame_get_buffer(f.c, C.int(align))) } -func (f *Frame) AllocHWBuffer(hfc *HardwareFrameContext, align int) error { - return newError(C.av_hwframe_get_buffer(hfc.c, f.c, C.int(align))) +func (f *Frame) AllocHardwareBuffer(hfc *HardwareFrameContext) error { + return newError(C.av_hwframe_get_buffer(hfc.c, f.c, 0)) } func (f *Frame) AllocImage(align int) error { diff --git a/hardware_frame_context.go b/hardware_frame_context.go index 39bbe2c..9aeb2ae 100644 --- a/hardware_frame_context.go +++ b/hardware_frame_context.go @@ -3,46 +3,49 @@ package astiav //#include import "C" import ( - "fmt" "unsafe" ) +// https://github.com/FFmpeg/FFmpeg/blob/n7.0/libavutil/hwcontext.h#L115 type HardwareFrameContext struct { c *C.struct_AVBufferRef } -func AllocHardwareFrameContext(hdc *HardwareDeviceContext) (*HardwareFrameContext, error) { - if hfc := C.av_hwframe_ctx_alloc(hdc.c); hfc != nil { - return &HardwareFrameContext{c: hfc}, nil +func newHardwareFrameContextFromC(c *C.struct_AVBufferRef) *HardwareFrameContext { + if c == nil { + return nil } - return nil, fmt.Errorf("failed to allocate hardware frame context") + return &HardwareFrameContext{c: c} +} + +func AllocHardwareFrameContext(hdc *HardwareDeviceContext) *HardwareFrameContext { + return newHardwareFrameContextFromC(C.av_hwframe_ctx_alloc(hdc.c)) +} + +func (hfc *HardwareFrameContext) data() *C.AVHWFramesContext { + return (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) } func (hfc *HardwareFrameContext) SetWidth(width int) { - frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) - frameCtx.width = C.int(width) + hfc.data().width = C.int(width) } func (hfc *HardwareFrameContext) SetHeight(height int) { - frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) - frameCtx.height = C.int(height) + hfc.data().height = C.int(height) } -func (hfc *HardwareFrameContext) SetFormat(format PixelFormat) { - frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) - frameCtx.format = C.enum_AVPixelFormat(format) +func (hfc *HardwareFrameContext) SetPixelFormat(format PixelFormat) { + hfc.data().format = C.enum_AVPixelFormat(format) } -func (hfc *HardwareFrameContext) SetSWFormat(swFormat PixelFormat) { - frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) - frameCtx.sw_format = C.enum_AVPixelFormat(swFormat) +func (hfc *HardwareFrameContext) SetSoftwarePixelFormat(swFormat PixelFormat) { + hfc.data().sw_format = C.enum_AVPixelFormat(swFormat) } func (hfc *HardwareFrameContext) SetInitialPoolSize(initialPoolSize int) { - frameCtx := (*C.AVHWFramesContext)(unsafe.Pointer((hfc.c.data))) - frameCtx.initial_pool_size = C.int(initialPoolSize) + hfc.data().initial_pool_size = C.int(initialPoolSize) } -func (hfc *HardwareFrameContext) Init() error { +func (hfc *HardwareFrameContext) Initialize() error { return newError(C.av_hwframe_ctx_init(hfc.c)) } From e1f7d0c7c2763e549231bb6ba4f9f63b388db277 Mon Sep 17 00:00:00 2001 From: l0rem1psum Date: Sun, 22 Sep 2024 11:14:01 +0000 Subject: [PATCH 3/5] feat(example): add hardware encoding example --- README.md | 1 + examples/hardware_encoding/main.go | 204 +++++++++++++++++++++++++++++ frame.go | 4 + 3 files changed, 209 insertions(+) create mode 100644 examples/hardware_encoding/main.go diff --git a/README.md b/README.md index 6ebe16d..7f8c98c 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Examples are located in the [examples](examples) directory and mirror as much as |Demuxing/Decoding|[see](examples/demuxing_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/demuxing_decoding.c) |Filtering|[see](examples/filtering/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/filtering_video.c) |Hardware Decoding|[see](examples/hardware_decoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/hw_decode.c) +|Hardware Encoding|[see](examples/hardware_encoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/vaapi_encode.c) |Remuxing|[see](examples/remuxing/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/remuxing.c) |Scaling|[see](examples/scaling/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/scaling_video.c) |Transcoding|[see](examples/transcoding/main.go)|[see](https://github.com/FFmpeg/FFmpeg/blob/n7.0/doc/examples/transcoding.c) diff --git a/examples/hardware_encoding/main.go b/examples/hardware_encoding/main.go new file mode 100644 index 0000000..75209e1 --- /dev/null +++ b/examples/hardware_encoding/main.go @@ -0,0 +1,204 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "log" + "os" + "strings" + + "github.com/asticode/go-astiav" +) + +var ( + encoderCodecName = flag.String("c", "", "the encoder codec name (e.g. h264_nvenc)") + hardwareDeviceName = flag.String("n", "", "the hardware device name (e.g. 0)") + hardwareDeviceTypeName = flag.String("t", "", "the hardware device type (e.g. cuda)") + hardwarePixelFormatName = flag.String("hpf", "", "the hardware pixel format name (e.g. cuda)") + + width = flag.Int("w", 1920, "the width") + height = flag.Int("h", 1080, "the height") + fps = flag.Int("f", 25, "the fps") + initialPoolSize = flag.Int("p", 20, "the initial pool size") + patternGridSize = flag.Int("g", 128, "the pattern grid size") + + output = flag.String("o", "", "the output path") +) + +func main() { + // Handle ffmpeg logs + astiav.SetLogLevel(astiav.LogLevelDebug) + astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) { + var cs string + if c != nil { + if cl := c.Class(); cl != nil { + cs = " - class: " + cl.String() + } + } + log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l) + }) + + // Parse flags + flag.Parse() + + // Usage + if *hardwareDeviceTypeName == "" || *encoderCodecName == "" || *hardwarePixelFormatName == "" || *output == "" { + log.Println("Usage: -t -c -hpf -o [-n -w -h -f -p -g ]") + return + } + + // Open output file + output, err := os.OpenFile(*output, os.O_CREATE|os.O_WRONLY, 0o644) + if err != nil { + log.Fatal(fmt.Errorf("main: opening output file failed: %w", err)) + } + defer output.Close() + + // Get hardware device type + hardwareDeviceType := astiav.FindHardwareDeviceTypeByName(*hardwareDeviceTypeName) + if hardwareDeviceType == astiav.HardwareDeviceTypeNone { + log.Fatal(errors.New("main: hardware device not found")) + } + + // Create hardware device context + hardwareDeviceContext, err := astiav.CreateHardwareDeviceContext(hardwareDeviceType, *hardwareDeviceName, nil) + if err != nil { + log.Fatal(fmt.Errorf("main: creating hardware device context failed: %w", err)) + } + + // Find encoder codec + encCodec := astiav.FindEncoderByName(*encoderCodecName) + if encCodec == nil { + log.Fatal("main: encoder codec is nil") + } + + // Alloc codec context + encCodecContext := astiav.AllocCodecContext(encCodec) + if encCodecContext == nil { + log.Fatal("main: codec context is nil") + } + defer encCodecContext.Free() + + // Set codec context + encCodecContext.SetWidth(*width) + encCodecContext.SetHeight(*height) + encCodecContext.SetTimeBase(astiav.NewRational(1, *fps)) + encCodecContext.SetFramerate(astiav.NewRational(*fps, 1)) + hardwarePixelFormatName := astiav.FindPixelFormatByName(*hardwarePixelFormatName) + if hardwarePixelFormatName == astiav.PixelFormatNone { + log.Fatal("main: hardware pixel format not found") + } + encCodecContext.SetPixelFormat(hardwarePixelFormatName) + + // Set hardware frame context + hardwareFrameCtx := astiav.AllocHardwareFrameContext(hardwareDeviceContext) + if hardwareFrameCtx == nil { + log.Fatal("main: hardware frame context is nil") + } + hardwareFrameCtx.SetPixelFormat(hardwarePixelFormatName) + hardwareFrameCtx.SetSoftwarePixelFormat(astiav.PixelFormatNv12) + hardwareFrameCtx.SetWidth(*width) + hardwareFrameCtx.SetHeight(*height) + hardwareFrameCtx.SetInitialPoolSize(*initialPoolSize) + + // Initialize hardware frame context + if err := hardwareFrameCtx.Initialize(); err != nil { + log.Fatal(fmt.Errorf("main: initializing hardware frame context failed: %w", err)) + } + encCodecContext.SetHardwareFrameContext(hardwareFrameCtx) + + // Open codec context + if err := encCodecContext.Open(encCodec, nil); err != nil { + log.Fatal(fmt.Errorf("main: opening codec context failed: %w", err)) + } + + frameIndex := 0 + + // Draw frames, upload them to hardware devices, and encode them + for { + // Alloc software frame + softwareFrame := astiav.AllocFrame() + + // Set software frame + softwareFrame.SetWidth(*width) + softwareFrame.SetHeight(*height) + softwareFrame.SetPixelFormat(astiav.PixelFormatNv12) + + // Alloc software frame buffer + if err := softwareFrame.AllocBuffer(0); err != nil { + log.Fatal(fmt.Errorf("main: allocating buffer failed: %w", err)) + } + + // Fill software frame + yPlane, uvPlane := MakeNV12MovingCheckerboardPattern(*width, *height, *patternGridSize, frameIndex) + softwareFrame.SetData(0, yPlane) + softwareFrame.SetData(1, uvPlane) + + // Alloc hardware frame + hardwareFrame := astiav.AllocFrame() + + // Alloc hardware frame buffer + if err := hardwareFrame.AllocHardwareBuffer(hardwareFrameCtx); err != nil { + log.Fatal(fmt.Errorf("main: allocating hardware buffer failed: %w", err)) + } + + // Upload software frame to hardware frame + if err := softwareFrame.TransferHardwareData(hardwareFrame); err != nil { + log.Fatal(fmt.Errorf("main: uploading from frame failed: %w", err)) + } + softwareFrame.Free() + + // Encode frame + if err := encCodecContext.SendFrame(hardwareFrame); err != nil { + log.Fatal(fmt.Errorf("main: sending frame failed: %w", err)) + } + hardwareFrame.Free() + + // Receive packet + packet := astiav.AllocPacket() + for { + if err := encCodecContext.ReceivePacket(packet); err != nil { + break + } + + // Write packet + if _, err := output.Write(packet.Data()); err != nil { + log.Fatal(fmt.Errorf("main: writing packet failed: %w", err)) + } + + packet.Unref() + } + packet.Free() + + frameIndex++ + log.Printf("Finished encoding frame %d\n", frameIndex) + } +} + +func MakeNV12MovingCheckerboardPattern(width, height, blockSize, frame int) ([]byte, []byte) { + yPlane := make([]byte, width*height) + uvPlane := make([]byte, width*height/2) + + xOffset := frame % blockSize + yOffset := frame % blockSize + + // Y plane (checkerboard pattern) + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + if ((x+xOffset)/blockSize+(y+yOffset)/blockSize)%2 == 0 { + yPlane[y*width+x] = 255 // White + } else { + yPlane[y*width+x] = 0 // Black + } + } + } + + // UV plane (gray) + for i := 0; i < len(uvPlane); i += 2 { + uvPlane[i] = 128 // U component (neutral) + uvPlane[i+1] = 128 // V component (neutral) + } + + return yPlane, uvPlane +} diff --git a/frame.go b/frame.go index c9b9660..ee91c15 100644 --- a/frame.go +++ b/frame.go @@ -73,6 +73,10 @@ func (f *Frame) Data() *FrameData { return newFrameData(newFrameDataFrame(f)) } +func (f *Frame) SetData(plane int, data []byte) { + f.c.data[plane] = (*C.uint8_t)(unsafe.Pointer(&data[0])) +} + func (f *Frame) Height() int { return int(f.c.height) } From e9e602f015ae701dbc2e7ce91c86861ab49717a4 Mon Sep 17 00:00:00 2001 From: Quentin Renard Date: Wed, 23 Oct 2024 12:21:23 +0200 Subject: [PATCH 4/5] Clean up --- examples/hardware_encoding/main.go | 164 +++++++++++------------------ frame.go | 4 - 2 files changed, 61 insertions(+), 107 deletions(-) diff --git a/examples/hardware_encoding/main.go b/examples/hardware_encoding/main.go index 75209e1..66fdb0a 100644 --- a/examples/hardware_encoding/main.go +++ b/examples/hardware_encoding/main.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "log" - "os" "strings" "github.com/asticode/go-astiav" @@ -16,14 +15,8 @@ var ( hardwareDeviceName = flag.String("n", "", "the hardware device name (e.g. 0)") hardwareDeviceTypeName = flag.String("t", "", "the hardware device type (e.g. cuda)") hardwarePixelFormatName = flag.String("hpf", "", "the hardware pixel format name (e.g. cuda)") - - width = flag.Int("w", 1920, "the width") - height = flag.Int("h", 1080, "the height") - fps = flag.Int("f", 25, "the fps") - initialPoolSize = flag.Int("p", 20, "the initial pool size") - patternGridSize = flag.Int("g", 128, "the pattern grid size") - - output = flag.String("o", "", "the output path") + height = flag.Int("h", 1080, "the height") + width = flag.Int("w", 1920, "the width") ) func main() { @@ -43,18 +36,11 @@ func main() { flag.Parse() // Usage - if *hardwareDeviceTypeName == "" || *encoderCodecName == "" || *hardwarePixelFormatName == "" || *output == "" { - log.Println("Usage: -t -c -hpf -o [-n -w -h -f -p -g ]") + if *hardwareDeviceTypeName == "" || *encoderCodecName == "" || *hardwarePixelFormatName == "" { + log.Println("Usage: -t -c -hpf [-n -w -h ]") return } - // Open output file - output, err := os.OpenFile(*output, os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - log.Fatal(fmt.Errorf("main: opening output file failed: %w", err)) - } - defer output.Close() - // Get hardware device type hardwareDeviceType := astiav.FindHardwareDeviceTypeByName(*hardwareDeviceTypeName) if hardwareDeviceType == astiav.HardwareDeviceTypeNone { @@ -83,122 +69,94 @@ func main() { // Set codec context encCodecContext.SetWidth(*width) encCodecContext.SetHeight(*height) - encCodecContext.SetTimeBase(astiav.NewRational(1, *fps)) - encCodecContext.SetFramerate(astiav.NewRational(*fps, 1)) + encCodecContext.SetTimeBase(astiav.NewRational(1, 25)) + encCodecContext.SetFramerate(encCodecContext.TimeBase().Invert()) hardwarePixelFormatName := astiav.FindPixelFormatByName(*hardwarePixelFormatName) if hardwarePixelFormatName == astiav.PixelFormatNone { log.Fatal("main: hardware pixel format not found") } encCodecContext.SetPixelFormat(hardwarePixelFormatName) - // Set hardware frame context - hardwareFrameCtx := astiav.AllocHardwareFrameContext(hardwareDeviceContext) - if hardwareFrameCtx == nil { + // Alloc hardware frame context + hardwareFrameContext := astiav.AllocHardwareFrameContext(hardwareDeviceContext) + if hardwareFrameContext == nil { log.Fatal("main: hardware frame context is nil") } - hardwareFrameCtx.SetPixelFormat(hardwarePixelFormatName) - hardwareFrameCtx.SetSoftwarePixelFormat(astiav.PixelFormatNv12) - hardwareFrameCtx.SetWidth(*width) - hardwareFrameCtx.SetHeight(*height) - hardwareFrameCtx.SetInitialPoolSize(*initialPoolSize) + + // Set hardware frame content + const softwarePixelFormat = astiav.PixelFormatNv12 + hardwareFrameContext.SetPixelFormat(hardwarePixelFormatName) + hardwareFrameContext.SetSoftwarePixelFormat(softwarePixelFormat) + hardwareFrameContext.SetWidth(*width) + hardwareFrameContext.SetHeight(*height) + hardwareFrameContext.SetInitialPoolSize(20) // Initialize hardware frame context - if err := hardwareFrameCtx.Initialize(); err != nil { + if err := hardwareFrameContext.Initialize(); err != nil { log.Fatal(fmt.Errorf("main: initializing hardware frame context failed: %w", err)) } - encCodecContext.SetHardwareFrameContext(hardwareFrameCtx) + + // Update hardware frame context + encCodecContext.SetHardwareFrameContext(hardwareFrameContext) // Open codec context if err := encCodecContext.Open(encCodec, nil); err != nil { log.Fatal(fmt.Errorf("main: opening codec context failed: %w", err)) } - frameIndex := 0 + // Alloc software frame + softwareFrame := astiav.AllocFrame() + defer softwareFrame.Free() - // Draw frames, upload them to hardware devices, and encode them - for { - // Alloc software frame - softwareFrame := astiav.AllocFrame() + // Set software frame + softwareFrame.SetWidth(*width) + softwareFrame.SetHeight(*height) + softwareFrame.SetPixelFormat(softwarePixelFormat) - // Set software frame - softwareFrame.SetWidth(*width) - softwareFrame.SetHeight(*height) - softwareFrame.SetPixelFormat(astiav.PixelFormatNv12) + // Alloc software frame buffer + if err := softwareFrame.AllocBuffer(0); err != nil { + log.Fatal(fmt.Errorf("main: allocating buffer failed: %w", err)) + } - // Alloc software frame buffer - if err := softwareFrame.AllocBuffer(0); err != nil { - log.Fatal(fmt.Errorf("main: allocating buffer failed: %w", err)) - } + // Fill software frame with black + if err = softwareFrame.ImageFillBlack(); err != nil { + log.Fatal(fmt.Errorf("main: filling software frame with black failed: %w", err)) + } - // Fill software frame - yPlane, uvPlane := MakeNV12MovingCheckerboardPattern(*width, *height, *patternGridSize, frameIndex) - softwareFrame.SetData(0, yPlane) - softwareFrame.SetData(1, uvPlane) + // Alloc hardware frame + hardwareFrame := astiav.AllocFrame() + defer hardwareFrame.Free() - // Alloc hardware frame - hardwareFrame := astiav.AllocFrame() + // Alloc hardware frame buffer + if err := hardwareFrame.AllocHardwareBuffer(hardwareFrameContext); err != nil { + log.Fatal(fmt.Errorf("main: allocating hardware buffer failed: %w", err)) + } - // Alloc hardware frame buffer - if err := hardwareFrame.AllocHardwareBuffer(hardwareFrameCtx); err != nil { - log.Fatal(fmt.Errorf("main: allocating hardware buffer failed: %w", err)) - } + // Transfer software frame data to hardware frame + if err := softwareFrame.TransferHardwareData(hardwareFrame); err != nil { + log.Fatal(fmt.Errorf("main: transferring hardware data failed: %w", err)) + } - // Upload software frame to hardware frame - if err := softwareFrame.TransferHardwareData(hardwareFrame); err != nil { - log.Fatal(fmt.Errorf("main: uploading from frame failed: %w", err)) - } - softwareFrame.Free() + // Encode frame + if err := encCodecContext.SendFrame(hardwareFrame); err != nil { + log.Fatal(fmt.Errorf("main: sending frame failed: %w", err)) + } - // Encode frame - if err := encCodecContext.SendFrame(hardwareFrame); err != nil { - log.Fatal(fmt.Errorf("main: sending frame failed: %w", err)) - } - hardwareFrame.Free() + // Alloc packet + pkt := astiav.AllocPacket() + defer pkt.Free() + // Loop + for { // Receive packet - packet := astiav.AllocPacket() - for { - if err := encCodecContext.ReceivePacket(packet); err != nil { + if err = encCodecContext.ReceivePacket(pkt); err != nil { + if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) { break } - - // Write packet - if _, err := output.Write(packet.Data()); err != nil { - log.Fatal(fmt.Errorf("main: writing packet failed: %w", err)) - } - - packet.Unref() + log.Fatal(fmt.Errorf("main: receiving packet failed: %w", err)) } - packet.Free() - frameIndex++ - log.Printf("Finished encoding frame %d\n", frameIndex) + // Log + log.Println("new packet") } } - -func MakeNV12MovingCheckerboardPattern(width, height, blockSize, frame int) ([]byte, []byte) { - yPlane := make([]byte, width*height) - uvPlane := make([]byte, width*height/2) - - xOffset := frame % blockSize - yOffset := frame % blockSize - - // Y plane (checkerboard pattern) - for y := 0; y < height; y++ { - for x := 0; x < width; x++ { - if ((x+xOffset)/blockSize+(y+yOffset)/blockSize)%2 == 0 { - yPlane[y*width+x] = 255 // White - } else { - yPlane[y*width+x] = 0 // Black - } - } - } - - // UV plane (gray) - for i := 0; i < len(uvPlane); i += 2 { - uvPlane[i] = 128 // U component (neutral) - uvPlane[i+1] = 128 // V component (neutral) - } - - return yPlane, uvPlane -} diff --git a/frame.go b/frame.go index ee91c15..c9b9660 100644 --- a/frame.go +++ b/frame.go @@ -73,10 +73,6 @@ func (f *Frame) Data() *FrameData { return newFrameData(newFrameDataFrame(f)) } -func (f *Frame) SetData(plane int, data []byte) { - f.c.data[plane] = (*C.uint8_t)(unsafe.Pointer(&data[0])) -} - func (f *Frame) Height() int { return int(f.c.height) } From 9a4988d231847eb04f4e748caac9dcf15a8e89aa Mon Sep 17 00:00:00 2001 From: Quentin Renard Date: Wed, 23 Oct 2024 12:29:12 +0200 Subject: [PATCH 5/5] Clean up --- examples/hardware_encoding/main.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/hardware_encoding/main.go b/examples/hardware_encoding/main.go index 66fdb0a..3e17a7f 100644 --- a/examples/hardware_encoding/main.go +++ b/examples/hardware_encoding/main.go @@ -16,6 +16,7 @@ var ( hardwareDeviceTypeName = flag.String("t", "", "the hardware device type (e.g. cuda)") hardwarePixelFormatName = flag.String("hpf", "", "the hardware pixel format name (e.g. cuda)") height = flag.Int("h", 1080, "the height") + softwarePixelFormatName = flag.String("spf", "", "the software pixel format name (e.g. nv12)") width = flag.Int("w", 1920, "the width") ) @@ -66,16 +67,18 @@ func main() { } defer encCodecContext.Free() + // Get hardware pixel format + hardwarePixelFormat := astiav.FindPixelFormatByName(*hardwarePixelFormatName) + if hardwarePixelFormat == astiav.PixelFormatNone { + log.Fatal("main: hardware pixel format not found") + } + // Set codec context encCodecContext.SetWidth(*width) encCodecContext.SetHeight(*height) encCodecContext.SetTimeBase(astiav.NewRational(1, 25)) encCodecContext.SetFramerate(encCodecContext.TimeBase().Invert()) - hardwarePixelFormatName := astiav.FindPixelFormatByName(*hardwarePixelFormatName) - if hardwarePixelFormatName == astiav.PixelFormatNone { - log.Fatal("main: hardware pixel format not found") - } - encCodecContext.SetPixelFormat(hardwarePixelFormatName) + encCodecContext.SetPixelFormat(hardwarePixelFormat) // Alloc hardware frame context hardwareFrameContext := astiav.AllocHardwareFrameContext(hardwareDeviceContext) @@ -83,9 +86,14 @@ func main() { log.Fatal("main: hardware frame context is nil") } + // Get software pixel format + softwarePixelFormat := astiav.FindPixelFormatByName(*softwarePixelFormatName) + if softwarePixelFormat == astiav.PixelFormatNone { + log.Fatal("main: software pixel format not found") + } + // Set hardware frame content - const softwarePixelFormat = astiav.PixelFormatNv12 - hardwareFrameContext.SetPixelFormat(hardwarePixelFormatName) + hardwareFrameContext.SetPixelFormat(hardwarePixelFormat) hardwareFrameContext.SetSoftwarePixelFormat(softwarePixelFormat) hardwareFrameContext.SetWidth(*width) hardwareFrameContext.SetHeight(*height) @@ -96,7 +104,7 @@ func main() { log.Fatal(fmt.Errorf("main: initializing hardware frame context failed: %w", err)) } - // Update hardware frame context + // Update encoder codec context hardware frame context encCodecContext.SetHardwareFrameContext(hardwareFrameContext) // Open codec context