From 762aa14362d3852857ea7d4db1cac2e8632aa386 Mon Sep 17 00:00:00 2001 From: aggresss Date: Tue, 17 Oct 2023 15:38:39 +0800 Subject: [PATCH] 2023-10-17 15:38:39 CST W42D2 --- README.md | 11 +- avcodec.go | 22 +-- avcodec_avfft.go | 4 +- avcodec_bsf.go | 154 +++++++++++++++++ avcodec_codec_id.go | 2 +- avcodec_codec_par.go | 2 +- avcodec_packet.go | 4 +- avdevice.go | 4 +- avfilter.go | 74 ++++++++- avformat.go | 35 ++-- avformat_avio.go | 30 +++- avresample.go | 164 ------------------ avresample_version.go | 12 -- avutil.go | 25 ++- avutil_channel_layout.go | 10 +- avutil_dict.go | 4 +- avutil_error.go | 5 +- avutil_frame.go | 16 +- avutil_imgutils.go | 203 +++++++++++++++++++++++ avutil_log.go | 99 +++++++++-- avutil_mathematics.go | 2 +- avutil_md5.go | 38 +++++ avutil_motion_vector.go | 173 +++++++++++++++++++ avutil_murmur3.go | 36 ++++ avutil_opt.go | 71 +++++--- avutil_parseutils.go | 72 ++++++++ avutil_pixdesc.go | 32 +++- avutil_pixelutils.go | 17 ++ avutil_pixfmt.go | 12 +- avutil_samplefmt.go | 2 +- examples/encode-audio/main.go | 2 +- examples/encode-video/main.go | 2 +- examples/filter-audio/main.go | 293 ++++++++++++++++++++++++++++++++- examples/scaling-video/main.go | 112 ++++++++++++- examples/transcode-aac/main.go | 2 +- ffmpeg.go | 2 +- postproc.go | 10 +- swresample.go | 6 +- swscale.go | 26 ++- 39 files changed, 1481 insertions(+), 309 deletions(-) delete mode 100644 avresample.go delete mode 100644 avresample_version.go create mode 100644 avutil_imgutils.go create mode 100644 avutil_md5.go create mode 100644 avutil_motion_vector.go create mode 100644 avutil_murmur3.go create mode 100644 avutil_parseutils.go create mode 100644 avutil_pixelutils.go diff --git a/README.md b/README.md index ffa2f68..05168a4 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,14 @@ Go bindings for FFmpeg. - macOS ```shell - brew install ffmpeg + brew install pkg-config ffmpeg ``` - Debian ```shell - apt install \ - libavdevice-dev libavformat-dev libavfilter-dev \ - libavresample-dev libavcodec-dev libpostproc-dev \ - libswscale-dev libswresample-dev libavutil-dev + sudo apt install build-essential pkg-config \ + libavdevice-dev libavformat-dev libavfilter-dev libavcodec-dev \ + libpostproc-dev libswscale-dev libswresample-dev libavutil-dev ``` - Custom @@ -44,7 +43,7 @@ func main() { ### Step 3: Get ffmpeg-go-dev ```shell -go get github.com/qrtc/ffmpeg-dev-go@4.4 +go get github.com/qrtc/ffmpeg-dev-go@ ``` ### Step 4: Verify diff --git a/avcodec.go b/avcodec.go index c92e4d2..82e6381 100644 --- a/avcodec.go +++ b/avcodec.go @@ -17,7 +17,7 @@ const ( ) // AvDiscard -type AvDiscard int32 +type AvDiscard = C.enum_AVDiscard const ( AVDISCARD_NONE = AvDiscard(C.AVDISCARD_NONE) @@ -30,7 +30,7 @@ const ( ) // AvAudioServiceType -type AvAudioServiceType int32 +type AvAudioServiceType = C.enum_AVAudioServiceType const ( AV_AUDIO_SERVICE_TYPE_MAIN = AvAudioServiceType(C.AV_AUDIO_SERVICE_TYPE_MAIN) @@ -215,8 +215,8 @@ func (avctx *AvCodecContext) SetPrivData(v unsafe.Pointer) { } // Custom: GetPrivDataAddr gets `AVCodecContext.priv_data` address. -func (avctx *AvCodecContext) GetPrivDataAddr() *unsafe.Pointer { - return (*unsafe.Pointer)(&avctx.priv_data) +func (avctx *AvCodecContext) GetPrivDataAddr() unsafe.Pointer { + return (unsafe.Pointer)(&avctx.priv_data) } // Custom: Get Opaque gets `AVCodecContext.opaque` value. @@ -230,8 +230,8 @@ func (avctx *AvCodecContext) SetOpaque(v unsafe.Pointer) { } // Custom: GetOpaqueAddr gets `AVCodecContext.opaque` address. -func (avctx *AvCodecContext) GetOpaqueAddr() *unsafe.Pointer { - return (*unsafe.Pointer)(&avctx.opaque) +func (avctx *AvCodecContext) GetOpaqueAddr() unsafe.Pointer { + return (unsafe.Pointer)(&avctx.opaque) } // Custom: Get BitRate gets `AVCodecContext.bit_rate` value. @@ -2176,8 +2176,8 @@ func (avctx *AvCodecContext) SetHwaccelContext(v unsafe.Pointer) { } // Custom: GetHwaccelContextAddr gets `AVCodecContext.hwaccel_context` address. -func (avctx *AvCodecContext) GetHwaccelContextAddr() *unsafe.Pointer { - return (*unsafe.Pointer)(&avctx.hwaccel_context) +func (avctx *AvCodecContext) GetHwaccelContextAddr() unsafe.Pointer { + return (unsafe.Pointer)(&avctx.hwaccel_context) } // Custom: Get DctAlgo gets `AVCodecContext.dct_algo` value. @@ -3168,7 +3168,7 @@ const ( type AvPicture C.struct_AVPicture // AvSubtitleType -type AvSubtitleType int32 +type AvSubtitleType = C.enum_AVSubtitleType const ( SUBTITLE_NONE = AvSubtitleType(C.SUBTITLE_NONE) @@ -3377,7 +3377,7 @@ func AvCodecGetHwFramesParameters(avctx *AvCodecContext, deviceRef *AvBufferRef, } // AvPictureStructure -type AvPictureStructure int32 +type AvPictureStructure = C.enum_AVPictureStructure const ( AV_PICTURE_STRUCTURE_UNKNOWN = AvPictureStructure(C.AV_PICTURE_STRUCTURE_UNKNOWN) @@ -3706,7 +3706,7 @@ func AvHwaccelNext(hwaccel *AvHWAccel) *AvHWAccel { } // AvLockOp -type AvLockOp int32 +type AvLockOp = C.enum_AVLockOp const ( AV_LOCK_CREATE = AvLockOp(C.AV_LOCK_CREATE) diff --git a/avcodec_avfft.go b/avcodec_avfft.go index fc85cd0..1d4d47f 100644 --- a/avcodec_avfft.go +++ b/avcodec_avfft.go @@ -59,7 +59,7 @@ func AvMdctEnd(s *FftContext) { C.av_mdct_end((*C.struct_FFTContext)(s)) } -type RDFTransformType int32 +type RDFTransformType = C.enum_RDFTransformType const ( DFT_R2C = RDFTransformType(C.DFT_R2C) @@ -88,7 +88,7 @@ func AvRdftEnd(r *RDFTContext) { type DCTContext C.struct_DCTContext -type DCTTransformType int32 +type DCTTransformType = C.enum_DCTTransformType const ( DCT_II = DCTTransformType(C.DCT_II) diff --git a/avcodec_bsf.go b/avcodec_bsf.go index 0fddf24..b264f7f 100644 --- a/avcodec_bsf.go +++ b/avcodec_bsf.go @@ -12,9 +12,163 @@ type AvBSFInternal C.struct_AVBSFInternal // AvBSFContext type AvBSFContext C.struct_AVBSFContext +// Custom: GetAvClass gets `AVBSFContext.av_class` value. +func (bsfc *AvBSFContext) GetAvClass() *AvClass { + return (*AvClass)(bsfc.av_class) +} + +// Custom: SetAvClass sets `AVBSFContext.av_class` value. +func (bsfc *AvBSFContext) SetAvClass(v *AvClass) { + bsfc.av_class = (*C.struct_AVClass)(v) +} + +// Custom: GetAvClassAddr gets `AVBSFContext.av_class` address. +func (bsfc *AvBSFContext) GetAvClassAddr() **AvClass { + return (**AvClass)(unsafe.Pointer(&bsfc.av_class)) +} + +// Custom: GetFilter gets `AVBSFContext.filter` value. +func (bsfc *AvBSFContext) GetFilter() *AvBitStreamFilter { + return (*AvBitStreamFilter)(bsfc.filter) +} + +// Custom: SetFilter sets `AVBSFContext.filter` value. +func (bsfc *AvBSFContext) SetFilter(v *AvBitStreamFilter) { + bsfc.filter = (*C.struct_AVBitStreamFilter)(v) +} + +// Custom: GetFilterAddr gets `AVBSFContext.filter` address. +func (bsfc *AvBSFContext) GetFilterAddr() **AvBitStreamFilter { + return (**AvBitStreamFilter)(unsafe.Pointer(&bsfc.filter)) +} + +// Custom: GetInternal gets `AVBSFContext.internal` value. +func (bsfc *AvBSFContext) GetInternal() *AvBSFInternal { + return (*AvBSFInternal)(bsfc.internal) +} + +// Custom: SetInternal sets `AVBSFContext.internal` value. +func (bsfc *AvBSFContext) SetInternal(v *AvBSFInternal) { + bsfc.internal = (*C.struct_AVBSFInternal)(v) +} + +// Custom: GetInternalAddr gets `AVBSFContext.internal` address. +func (bsfc *AvBSFContext) GetInternalAddr() **AvBSFInternal { + return (**AvBSFInternal)(unsafe.Pointer(&bsfc.internal)) +} + +// Custom: GetPrivData gets `AVBSFContext.priv_data` value. +func (bsfc *AvBSFContext) GetPrivData() unsafe.Pointer { + return bsfc.priv_data +} + +// Custom: SetPrivData sets `AVBSFContext.priv_data` value. +func (bsfc *AvBSFContext) SetPrivData(v unsafe.Pointer) { + bsfc.priv_data = v +} + +// Custom: GetPrivDataAddr gets `AVBSFContext.priv_data` address. +func (bsfc *AvBSFContext) GetPrivDataAddr() unsafe.Pointer { + return (unsafe.Pointer)(&bsfc.priv_data) +} + +// Custom: GetParIn gets `AVBSFContext.par_in` value. +func (bsfc *AvBSFContext) GetParIn() *AvCodecParameters { + return (*AvCodecParameters)(bsfc.par_in) +} + +// Custom: SetParIn sets `AVBSFContext.par_in` value. +func (bsfc *AvBSFContext) SetParIn(v *AvCodecParameters) { + bsfc.par_in = (*C.struct_AVCodecParameters)(v) +} + +// Custom: GetParInAddr gets `AVBSFContext.par_in` address. +func (bsfc *AvBSFContext) GetParInAddr() **AvCodecParameters { + return (**AvCodecParameters)(unsafe.Pointer(&bsfc.par_in)) +} + +// Custom: GetParOut gets `AVBSFContext.par_out` value. +func (bsfc *AvBSFContext) GetParOut() *AvCodecParameters { + return (*AvCodecParameters)(bsfc.par_out) +} + +// Custom: SetParOut sets `AVBSFContext.par_out` value. +func (bsfc *AvBSFContext) SetParOut(v *AvCodecParameters) { + bsfc.par_out = (*C.struct_AVCodecParameters)(v) +} + +// Custom: GetParOutAddr gets `AVBSFContext.par_out` address. +func (bsfc *AvBSFContext) GetParOutAddr() **AvCodecParameters { + return (**AvCodecParameters)(unsafe.Pointer(&bsfc.par_out)) +} + +// Custom: GetTimeBaseIn gets `AVBSFContext.time_base_in` value. +func (bsfc *AvBSFContext) GetTimeBaseIn() AvRational { + return (AvRational)(bsfc.time_base_in) +} + +// Custom: SetTimeBaseIn sets `AVBSFContext.time_base_in` value. +func (bsfc *AvBSFContext) SetTimeBaseIn(v AvRational) { + bsfc.time_base_in = (C.AVRational)(v) +} + +// Custom: GetTimeBaseInAddr gets `AVBSFContext.time_base_in` address. +func (bsfc *AvBSFContext) GetTimeBaseInAddr() *AvRational { + return (*AvRational)(&bsfc.time_base_in) +} + +// Custom: GetTimeBaseOut gets `AVBSFContext.time_base_out` value. +func (bsfc *AvBSFContext) GetTimeBaseOut() AvRational { + return (AvRational)(bsfc.time_base_out) +} + +// Custom: SetTimeBaseOut sets `AVBSFContext.time_base_out` value. +func (bsfc *AvBSFContext) SetTimeBaseOut(v AvRational) { + bsfc.time_base_out = (C.AVRational)(v) +} + +// Custom: GetTimeBaseOutAddr gets `AVBSFContext.time_base_out` address. +func (bsfc *AvBSFContext) GetTimeBaseOutAddr() *AvRational { + return (*AvRational)(&bsfc.time_base_out) +} + // AvBitStreamFilter type AvBitStreamFilter C.struct_AVBitStreamFilter +// Custom: GetName gets `AVBitStreamFilter.name` value. +func (bsf *AvBitStreamFilter) GetName() string { + return C.GoString(bsf.name) +} + +// Custom: GetCodecIds gets `AVBitStreamFilter.codec_ids` value. +func (bsf *AvBitStreamFilter) GetCodecIds() (v []AvCodecID) { + if bsf.codec_ids == nil { + return v + } + ptr := (*AvCodecID)(bsf.codec_ids) + for *ptr != AV_CODEC_ID_NONE { + v = append(v, *ptr) + ptr = (*AvCodecID)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + + uintptr(unsafe.Sizeof(*ptr)))) + } + return v +} + +// Custom: GetPrivClass gets `AVBitStreamFilter.priv_class` value. +func (bsf *AvBitStreamFilter) GetPrivClass() *AvClass { + return (*AvClass)(bsf.priv_class) +} + +// Custom: SetPrivClass sets `AVBitStreamFilter.priv_class` value. +func (bsf *AvBitStreamFilter) SetPrivClass(v *AvClass) { + bsf.priv_class = (*C.struct_AVClass)(v) +} + +// Custom: GetPrivClassAddr gets `AVBitStreamFilter.priv_class` address. +func (bsf *AvBitStreamFilter) GetPrivClassAddr() **AvClass { + return (**AvClass)(unsafe.Pointer(&bsf.priv_class)) +} + // AvBsfGetByName returns a bitstream filter with the specified name or NULL if no such // bitstream filter exists. func AvBsfGetByName(name string) *AvBitStreamFilter { diff --git a/avcodec_codec_id.go b/avcodec_codec_id.go index cfc6ffa..152b895 100644 --- a/avcodec_codec_id.go +++ b/avcodec_codec_id.go @@ -6,7 +6,7 @@ package ffmpeg import "C" // AvCodecID -type AvCodecID int32 +type AvCodecID = C.enum_AVCodecID const ( AV_CODEC_ID_NONE = AvCodecID(C.AV_CODEC_ID_NONE) diff --git a/avcodec_codec_par.go b/avcodec_codec_par.go index 4528cf6..34c53b2 100644 --- a/avcodec_codec_par.go +++ b/avcodec_codec_par.go @@ -6,7 +6,7 @@ package ffmpeg import "C" import "unsafe" -type AvFieldOrder int32 +type AvFieldOrder = C.enum_AVFieldOrder const ( AV_FIELD_UNKNOWN = AvFieldOrder(C.AV_FIELD_UNKNOWN) diff --git a/avcodec_packet.go b/avcodec_packet.go index ffd55de..e700bd8 100644 --- a/avcodec_packet.go +++ b/avcodec_packet.go @@ -7,7 +7,7 @@ import "C" import "unsafe" // AvPacketSideDataType -type AvPacketSideDataType int32 +type AvPacketSideDataType = C.enum_AVPacketSideDataType const ( AV_PKT_DATA_PALETTE = AvPacketSideDataType(C.AV_PKT_DATA_PALETTE) @@ -247,7 +247,7 @@ const ( ) // AvSideDataParamChangeFlags -type AvSideDataParamChangeFlags int32 +type AvSideDataParamChangeFlags = C.enum_AVSideDataParamChangeFlags const ( AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT = AvSideDataParamChangeFlags(C.AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) diff --git a/avdevice.go b/avdevice.go index f2b3e69..97a6f53 100644 --- a/avdevice.go +++ b/avdevice.go @@ -50,7 +50,7 @@ func AvOutputVideoDeviceNext(d *AvOutputFormat) *AvOutputFormat { type AvDeviceRect C.struct_AVDeviceRect // AvAppToDevMessageType -type AvAppToDevMessageType int32 +type AvAppToDevMessageType = C.enum_AVAppToDevMessageType const ( AV_APP_TO_DEV_NONE = AvAppToDevMessageType(C.AV_APP_TO_DEV_NONE) @@ -68,7 +68,7 @@ const ( ) // AvDevToAppMessageType -type AvDevToAppMessageType int32 +type AvDevToAppMessageType = C.enum_AVDevToAppMessageType const ( AV_DEV_TO_APP_NONE = AvDevToAppMessageType(C.AV_DEV_TO_APP_NONE) diff --git a/avfilter.go b/avfilter.go index 704f450..1ecd712 100644 --- a/avfilter.go +++ b/avfilter.go @@ -59,6 +59,76 @@ const ( // callback functions used to interact with the filter. type AvFilter C.struct_AVFilter +// Custom: GetName gets `AVMotionVector.name` value. +func (flt *AvFilter) GetName() string { + return C.GoString(flt.name) +} + +// Custom: GetDescription gets `AVMotionVector.description` value. +func (flt *AvFilter) GetDescription() string { + return C.GoString(flt.description) +} + +// Custom: GetInputs gets `AVMotionVector.inputs` value. +func (flt *AvFilter) GetInputs() *AvFilterPad { + return (*AvFilterPad)(flt.inputs) +} + +// Custom: SetInputs sets `AVMotionVector.inputs` value. +func (flt *AvFilter) SetInputs(v *AvFilterPad) { + flt.inputs = (*C.struct_AVFilterPad)(v) +} + +// Custom: GetInputsAddr gets `AVMotionVector.inputs` address. +func (flt *AvFilter) GetInputsAddr() **AvFilterPad { + return (**AvFilterPad)(unsafe.Pointer(&flt.inputs)) +} + +// Custom: GetOutputs gets `AVMotionVector.outputs` value. +func (flt *AvFilter) GetOutputs() *AvFilterPad { + return (*AvFilterPad)(flt.outputs) +} + +// Custom: SetOutputs sets `AVMotionVector.outputs` value. +func (flt *AvFilter) SetOutputs(v *AvFilterPad) { + flt.outputs = (*C.struct_AVFilterPad)(v) +} + +// Custom: GetOutputsAddr gets `AVMotionVector.outputs` address. +func (flt *AvFilter) GetOutputsAddr() **AvFilterPad { + return (**AvFilterPad)(unsafe.Pointer(&flt.outputs)) +} + +// Custom: GetPrivClass gets `AVMotionVector.priv_class` value. +func (flt *AvFilter) GetPrivClass() *AvClass { + return (*AvClass)(flt.priv_class) +} + +// Custom: SetPrivClass sets `AVMotionVector.priv_class` value. +func (flt *AvFilter) SetPrivClass(v *AvClass) { + flt.priv_class = (*C.struct_AVClass)(v) +} + +// Custom: GetPrivClassAddr gets `AVMotionVector.priv_class` address. +func (flt *AvFilter) GetPrivClassAddr() **AvClass { + return (**AvClass)(unsafe.Pointer(&flt.priv_class)) +} + +// Custom: GetFlags gets `AVMotionVector.flags` value. +func (flt *AvFilter) GetFlags() int32 { + return (int32)(flt.flags) +} + +// Custom: SetFlags sets `AVMotionVector.flags` value. +func (flt *AvFilter) SetFlags(v int32) { + flt.flags = (C.int)(v) +} + +// Custom: GetFlagsAddr gets `AVMotionVector.flags` address. +func (flt *AvFilter) GetFlagsAddr() *int32 { + return (*int32)(&flt.flags) +} + const ( AVFILTER_THREAD_SLICE = C.AVFILTER_THREAD_SLICE ) @@ -75,8 +145,8 @@ type AvFilterFormatsConfig C.struct_AVFilterFormatsConfig // AvFilterLink type AvFilterLink C.struct_AVFilterLink -// AvFilterContextLink links two filters together. -func AvFilterContextLink(src *AvFilterContext, srcpad uint32, +// AvFilterLink2 links two filters together. +func AvFilterLink2(src *AvFilterContext, srcpad uint32, dst *AvFilterContext, dstpad uint32) int32 { return (int32)(C.avfilter_link((*C.struct_AVFilterContext)(src), (C.uint)(srcpad), (*C.struct_AVFilterContext)(dst), (C.uint)(dstpad))) diff --git a/avformat.go b/avformat.go index faa5dea..ff36342 100644 --- a/avformat.go +++ b/avformat.go @@ -208,7 +208,7 @@ func (ifmt *AvInputFormat) GetMimeType() string { } // AvStreamParseType -type AvStreamParseType int32 +type AvStreamParseType = C.enum_AVStreamParseType const ( AVSTREAM_PARSE_NONE = AvStreamParseType(C.AVSTREAM_PARSE_NONE) @@ -312,8 +312,8 @@ func (stm *AvStream) SetPrivData(v unsafe.Pointer) { } // Custom: GetPrivDataAddr gets `AVStream.priv_data` address. -func (stm *AvStream) GetPrivDataAddr() *unsafe.Pointer { - return &stm.priv_data +func (stm *AvStream) GetPrivDataAddr() unsafe.Pointer { + return (unsafe.Pointer)(&stm.priv_data) } // Custom: GetTimeBase gets `AVStream.time_base` value. @@ -831,7 +831,7 @@ type AvFormatControlMessageFunc C.av_format_control_message type AvOpenCallbackFunc C.AVOpenCallback // AvDurationEstimationMethod -type AvDurationEstimationMethod int32 +type AvDurationEstimationMethod = C.enum_AVDurationEstimationMethod const ( AVFMT_DURATION_FROM_PTS = AvDurationEstimationMethod(C.AVFMT_DURATION_FROM_PTS) @@ -901,8 +901,8 @@ func (s *AvFormatContext) SetPrivData(v unsafe.Pointer) { } // Custom: GetPrivDataAddr gets `AVFormatContext.priv_data` address. -func (s *AvFormatContext) GetPrivDataAddr() *unsafe.Pointer { - return (*unsafe.Pointer)(unsafe.Pointer(&s.priv_data)) +func (s *AvFormatContext) GetPrivDataAddr() unsafe.Pointer { + return (unsafe.Pointer)(&s.priv_data) } // Custom: GetPb gets `AVFormatContext.pb` value. @@ -990,7 +990,7 @@ func (s *AvFormatContext) SetUrl(v string) { if s.url != nil { C.free(unsafe.Pointer(s.url)) } - s.url = vPtr + s.url = (*C.char)(vPtr) } // Custom: GetStartTime gets `AVFormatContext.start_time` value. @@ -1782,8 +1782,8 @@ func (s *AvFormatContext) SetOpaque(v unsafe.Pointer) { } // Custom: GetOpaqueAddr gets `AVFormatContext.opaque` address. -func (s *AvFormatContext) GetOpaqueAddr() *unsafe.Pointer { - return (*unsafe.Pointer)(&s.opaque) +func (s *AvFormatContext) GetOpaqueAddr() unsafe.Pointer { + return (unsafe.Pointer)(&s.opaque) } // Custom: GetOutputTsOffset gets `AVFormatContext.output_ts_offset` value. @@ -2336,16 +2336,23 @@ func AvGetOutputTimestamp(ic *AvFormatContext, stream int32, dts, wall *int64) i (C.int)(stream), (*C.int64_t)(dts), (*C.int64_t)(wall))) } -// TODO. av_hex_dump +// AvHexDump sends a nice hexadecimal dump of a buffer to the specified file stream. +func AvHexDump(f *FILE, buf *uint8, size int32) { + C.av_hex_dump((*C.FILE)(f), (*C.uint8_t)(buf), (C.int)(size)) +} -// AvHexDumpLog +// AvHexDumpLog sends a nice hexadecimal dump of a buffer to the log. func AvHexDumpLog(avcl unsafe.Pointer, level int32, buf *uint8, size int32) { C.av_hex_dump_log(avcl, (C.int)(level), (*C.uint8_t)(buf), (C.int)(size)) } -// TODO. av_pkt_dump2 +// AvPktDump2 sends a nice dump of a packet to the specified file stream. +func AvPktDump2(f *FILE, pkt *AvPacket, dumpPayload int32, st *AvStream) { + C.av_pkt_dump2((*C.FILE)(f), (*C.struct_AVPacket)(pkt), + (C.int)(dumpPayload), (*C.struct_AVStream)(st)) +} -// AvPktDumpLog2 +// AvPktDumpLog2 sends a nice dump of a packet to the log. func av_pkt_dump_log2(avcl unsafe.Pointer, level int32, pkt *AvPacket, dumpPayload int32, st *AvStream) { C.av_pkt_dump_log2(avcl, (C.int)(level), (*C.struct_AVPacket)(pkt), (C.int)(dumpPayload), (*C.struct_AVStream)(st)) @@ -2526,7 +2533,7 @@ func AvApplyBitstreamFilters(codec *AvCodecContext, pkt *AvPacket, bsfc *AvBitSt } // AvTimebaseSource -type AvTimebaseSource int32 +type AvTimebaseSource = C.enum_AVTimebaseSource const ( AVFMT_TBCF_AUTO = AvTimebaseSource(C.AVFMT_TBCF_AUTO) diff --git a/avformat_avio.go b/avformat_avio.go index f7d48f1..721623a 100644 --- a/avformat_avio.go +++ b/avformat_avio.go @@ -6,9 +6,16 @@ package ffmpeg typedef int (*avio_context_read_packet_func)(void *opaque, uint8_t *buf, int buf_size); typedef int (*avio_context_write_packet_func)(void *opaque, uint8_t *buf, int buf_size); typedef int64_t (*avio_context_seek_func)(void *opaque, int64_t offset, int whence); + +int avio_printf_wrap(AVIOContext *s, const char *fmt) { + return avio_printf(s, fmt, NULL); +} */ import "C" -import "unsafe" +import ( + "fmt" + "unsafe" +) const ( AVIO_SEEKABLE_NORMAL = C.AVIO_SEEKABLE_NORMAL @@ -22,7 +29,7 @@ type AvIOInterruptCB C.struct_AVIOInterruptCB type AvIODirEntry C.struct_AVIODirEntry // AvIODirEntryType -type AvIODirEntryType int32 +type AvIODirEntryType = C.enum_AVIODirEntryType const ( AVIO_ENTRY_UNKNOWN = AvIODirEntryType(C.AVIO_ENTRY_UNKNOWN) @@ -42,7 +49,7 @@ const ( type AvIODirContext C.struct_AVIODirContext // AvIODataMarkerType -type AvIODataMarkerType int32 +type AvIODataMarkerType = C.enum_AVIODataMarkerType const ( AVIO_DATA_MARKER_HEADER = AvIODataMarkerType(C.AVIO_DATA_MARKER_HEADER) @@ -247,11 +254,22 @@ func AvIOFeof(s *AvIOContext) int32 { return (int32)(C.avio_feof((*C.struct_AVIOContext)(s))) } -// TODO. avio_printf +// AvIOPrintf Writes a formatted string to the context. +func AvIOPrintf(s *AvIOContext, _fmt string, va ...any) int32 { + fmtPtr, fmtFunc := StringCasting(fmt.Sprintf(_fmt, va...)) + defer fmtFunc() + return (int32)(C.avio_printf_wrap((*C.struct_AVIOContext)(s), (*C.char)(fmtPtr))) +} -// TODO. avio_print_string_array +// NONEED: avio_print_string_array -// TODO. avio_print +// AvIOPrint +func AvIOPrint(s *AvIOContext, va ...any) { + fmtPtr, fmtFunc := StringCasting(fmt.Sprint(va...)) + defer fmtFunc() + fmtArray := []*C.char{(*C.char)(fmtPtr), nil} + C.avio_print_string_array((*C.struct_AVIOContext)(s), &fmtArray[0]) +} // AvIOFlush forces flushing of buffered data. func AvIOFlush(s *AvIOContext) { diff --git a/avresample.go b/avresample.go deleted file mode 100644 index 2733daa..0000000 --- a/avresample.go +++ /dev/null @@ -1,164 +0,0 @@ -package ffmpeg - -/* -#include -*/ -import "C" -import "unsafe" - -const ( - AVRESAMPLE_MAX_CHANNELS = C.AVRESAMPLE_MAX_CHANNELS -) - -type AvAudioResampleContext C.struct_AVAudioResampleContext - -// Deprecated: Use libswresample -type AvMixCoeffType int32 - -const ( - AV_MIX_COEFF_TYPE_Q8 = AvMixCoeffType(C.AV_MIX_COEFF_TYPE_Q8) - AV_MIX_COEFF_TYPE_Q15 = AvMixCoeffType(C.AV_MIX_COEFF_TYPE_Q15) - AV_MIX_COEFF_TYPE_FLT = AvMixCoeffType(C.AV_MIX_COEFF_TYPE_FLT) - AV_MIX_COEFF_TYPE_NB = AvMixCoeffType(C.AV_MIX_COEFF_TYPE_NB) -) - -// Deprecated: Use libswresample -type AvResampleFilterType int32 - -const ( - AV_RESAMPLE_FILTER_TYPE_CUBIC = AvResampleFilterType(C.AV_RESAMPLE_FILTER_TYPE_CUBIC) - AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL = AvResampleFilterType(C.AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL) - AV_RESAMPLE_FILTER_TYPE_KAISER = AvResampleFilterType(C.AV_RESAMPLE_FILTER_TYPE_KAISER) -) - -type AvResampleDitherMethod int32 - -const ( - AV_RESAMPLE_DITHER_NONE = AvResampleDitherMethod(C.AV_RESAMPLE_DITHER_NONE) - AV_RESAMPLE_DITHER_RECTANGULAR = AvResampleDitherMethod(C.AV_RESAMPLE_DITHER_RECTANGULAR) - AV_RESAMPLE_DITHER_TRIANGULAR = AvResampleDitherMethod(C.AV_RESAMPLE_DITHER_TRIANGULAR) - AV_RESAMPLE_DITHER_TRIANGULAR_HP = AvResampleDitherMethod(C.AV_RESAMPLE_DITHER_TRIANGULAR_HP) - AV_RESAMPLE_DITHER_TRIANGULAR_NS = AvResampleDitherMethod(C.AV_RESAMPLE_DITHER_TRIANGULAR_NS) - AV_RESAMPLE_DITHER_NB = AvResampleDitherMethod(C.AV_RESAMPLE_DITHER_NB) -) - -// Deprecated: Use libswresample -func AvResampleVersion() uint32 { - return (uint32)(C.avresample_version()) -} - -// Deprecated: Use libswresample -func AvResampleConfiguration() string { - return C.GoString(C.avresample_configuration()) -} - -// Deprecated: Use libswresample -func AvResampleLicense() string { - return C.GoString(C.avresample_license()) -} - -// Deprecated: Use libswresample -func AvResampleGetClass() *AvClass { - return (*AvClass)(C.avresample_get_class()) -} - -// Deprecated: Use libswresample -func AvResampleAllocContext() *AvAudioResampleContext { - return (*AvAudioResampleContext)(C.avresample_alloc_context()) -} - -// Deprecated: Use libswresample -func AvResampleOpen(avr *AvAudioResampleContext) int32 { - return (int32)(C.avresample_open((*C.struct_AVAudioResampleContext)(avr))) -} - -// Deprecated: Use libswresample -func AvResampleIsOpen(avr *AvAudioResampleContext) int32 { - return (int32)(C.avresample_is_open((*C.struct_AVAudioResampleContext)(avr))) -} - -// Deprecated: Use libswresample -func AvResampleClose(avr *AvAudioResampleContext) { - C.avresample_close((*C.struct_AVAudioResampleContext)(avr)) -} - -// Deprecated: Use libswresample -func AvResampleFree(avr **AvAudioResampleContext) { - C.avresample_free((**C.struct_AVAudioResampleContext)(unsafe.Pointer(avr))) -} - -// Deprecated: Use libswresample -func AvResampleBuildMatrix(inLayout, outLayout uint64, - centerMixLevel, surroundMixLevel, lfeMixLevel float64, - normalize int32, matrix *float64, stride int32, matrixEncoding AvMatrixEncoding) int32 { - return (int32)(C.avresample_build_matrix((C.uint64_t)(inLayout), (C.uint64_t)(outLayout), - (C.double)(centerMixLevel), (C.double)(surroundMixLevel), (C.double)(lfeMixLevel), - (C.int)(normalize), (*C.double)(matrix), (C.int)(stride), - (C.enum_AVMatrixEncoding)(matrixEncoding))) -} - -// Deprecated: Use libswresample -func AvResampleGetMatrix(avr *AvAudioResampleContext, matrix *float64, stride int32) int32 { - return (int32)(C.avresample_get_matrix((*C.struct_AVAudioResampleContext)(avr), - (*C.double)(matrix), (C.int)(stride))) -} - -// Deprecated: Use libswresample -func AvResampleSetMatrix(avr *AvAudioResampleContext, matrix *float64, stride int32) int32 { - return (int32)(C.avresample_set_matrix((*C.struct_AVAudioResampleContext)(avr), - (*C.double)(matrix), (C.int)(stride))) -} - -// Deprecated: Use libswresample -func AvResampleSetChannelMapping(avr *AvAudioResampleContext, channelMap *int32) int32 { - return (int32)(C.avresample_set_channel_mapping((*C.struct_AVAudioResampleContext)(avr), - (*C.int)(channelMap))) -} - -// Deprecated: Use libswresample -func AvResampleSetCompensation(avr *AvAudioResampleContext, sampleDelta, compensationDistance int32) int32 { - return (int32)(C.avresample_set_compensation((*C.struct_AVAudioResampleContext)(avr), - (C.int)(sampleDelta), (C.int)(compensationDistance))) -} - -// Deprecated: Use libswresample -func AvResampleGetOutSamples(avr *AvAudioResampleContext, inNbSamples int32) int32 { - return (int32)(C.avresample_get_out_samples((*C.struct_AVAudioResampleContext)(avr), - (C.int)(inNbSamples))) -} - -// Deprecated: Use libswresample -func AvResampleConvert(avr *AvAudioResampleContext, output **uint8, outPlaneSize, outSamples int32, - input **uint8, inPlaneSize, inSamples int32) int32 { - return (int32)(C.avresample_convert((*C.struct_AVAudioResampleContext)(avr), - (**C.uint8_t)(unsafe.Pointer(output)), (C.int)(outPlaneSize), (C.int)(outSamples), - (**C.uint8_t)(unsafe.Pointer(input)), (C.int)(inPlaneSize), (C.int)(inSamples))) -} - -// Deprecated: Use libswresample -func AvResampleGetDelay(avr *AvAudioResampleContext) int32 { - return (int32)(C.avresample_get_delay((*C.struct_AVAudioResampleContext)(avr))) -} - -// Deprecated: Use libswresample -func AvResampleAvailable(avr *AvAudioResampleContext) int32 { - return (int32)(C.avresample_available((*C.struct_AVAudioResampleContext)(avr))) -} - -// Deprecated: Use libswresample -func AvResampleRead(avr *AvAudioResampleContext, output **uint8, nbSamples int32) int32 { - return (int32)(C.avresample_read((*C.struct_AVAudioResampleContext)(avr), - (**C.uint8_t)(unsafe.Pointer(output)), (C.int)(nbSamples))) -} - -// Deprecated: Use libswresample -func AvResampleConvertFrame(avr *AvAudioResampleContext, output, input *AvFrame) int32 { - return (int32)(C.avresample_convert_frame((*C.struct_AVAudioResampleContext)(avr), - (*C.struct_AVFrame)(output), (*C.struct_AVFrame)(input))) -} - -// Deprecated: Use libswresample -func AvResampleConfig(avr *AvAudioResampleContext, out, in *AvFrame) int32 { - return (int32)(C.avresample_config((*C.struct_AVAudioResampleContext)(avr), - (*C.struct_AVFrame)(out), (*C.struct_AVFrame)(in))) -} diff --git a/avresample_version.go b/avresample_version.go deleted file mode 100644 index 9fa8b77..0000000 --- a/avresample_version.go +++ /dev/null @@ -1,12 +0,0 @@ -package ffmpeg - -/* -#include -*/ -import "C" - -const ( - LIBAVRESAMPLE_VERSION_MAJOR = C.LIBAVRESAMPLE_VERSION_MAJOR - LIBAVRESAMPLE_VERSION_MINOR = C.LIBAVRESAMPLE_VERSION_MINOR - LIBAVRESAMPLE_VERSION_MICRO = C.LIBAVRESAMPLE_VERSION_MICRO -) diff --git a/avutil.go b/avutil.go index 2073937..adf4da9 100644 --- a/avutil.go +++ b/avutil.go @@ -4,7 +4,11 @@ package ffmpeg #include */ import "C" -import "unsafe" +import ( + "unsafe" +) + +type FILE C.FILE // AvutilVersion returns the LIBAVUTIL_VERSION_INT constant. func AvutilVersion() uint32 { @@ -27,7 +31,7 @@ func AvutilLicense() string { } // Media Type -type AvMediaType int32 +type AvMediaType = C.enum_AVMediaType const ( AVMEDIA_TYPE_UNKNOWN = AvMediaType(C.AVMEDIA_TYPE_UNKNOWN) @@ -64,7 +68,7 @@ var ( ) // AvPictureType, pixel formats and basic image planes manipulation. -type AvPictureType int32 +type AvPictureType = C.enum_AVPictureType const ( AV_PICTURE_TYPE_NONE = AvPictureType(C.AV_PICTURE_TYPE_NONE) @@ -93,9 +97,20 @@ func AvIntListLengthForSize(elsize uint32, list unsafe.Pointer, term uint64) uin return (uint32)(C.av_int_list_length_for_size((C.uint)(elsize), list, (C.uint64_t)(term))) } -// TODO. av_int_list_length +// AvIntListLength +func AvIntListLength[T any](list *T, term uint64) uint32 { + elsize := unsafe.Sizeof(*list) + return (uint32)(C.av_int_list_length_for_size((C.uint)(elsize), (unsafe.Pointer)(list), (C.uint64_t)(term))) +} -// TODO. av_fopen_utf8 +// AvFopenUtf8 +func AvFopenUtf8(path, mode string) *FILE { + pathPtr, pathFunc := StringCasting(path) + defer pathFunc() + modePtr, modeFunc := StringCasting(mode) + defer modeFunc() + return (*FILE)(C.av_fopen_utf8((*C.char)(pathPtr), (*C.char)(modePtr))) +} // AvGetTimeBaseQ returns the fractional representation of the internal time base. func AvGetTimeBaseQ() AvRational { diff --git a/avutil_channel_layout.go b/avutil_channel_layout.go index 7fb5b86..2061a38 100644 --- a/avutil_channel_layout.go +++ b/avutil_channel_layout.go @@ -73,7 +73,7 @@ const ( AV_CH_LAYOUT_22POINT2 = uint64(C.AV_CH_LAYOUT_22POINT2) ) -type AvMatrixEncoding int32 +type AvMatrixEncoding = C.enum_AVMatrixEncoding const ( AV_MATRIX_ENCODING_NONE = AvMatrixEncoding(C.AV_MATRIX_ENCODING_NONE) @@ -101,10 +101,14 @@ func AvGetExtendedChannelLayout(name string, channelLayout *uint64, nbChannels * (*C.uint64_t)(channelLayout), (*C.int32_t)(nbChannels))) } +const AV_CH_LAYOUT_MAX_STRING_SIZE = 256 + // AvGetChannelLayoutString returns a description of a channel layout. -func AvGetChannelLayoutString(buf *int8, bufSize, nbChannels int32, channelLayout uint64) { - C.av_get_channel_layout_string((*C.char)(buf), (C.int)(bufSize), +func AvGetChannelLayoutString(nbChannels int32, channelLayout uint64) string { + buf := make([]C.char, AV_CH_LAYOUT_MAX_STRING_SIZE) + C.av_get_channel_layout_string((*C.char)(&buf[0]), (C.int)(AV_CH_LAYOUT_MAX_STRING_SIZE), (C.int)(nbChannels), (C.uint64_t)(channelLayout)) + return C.GoString((*C.char)(&buf[0])) } // AvBprintChannelLayout appends a description of a channel layout to a bprint buffer. diff --git a/avutil_dict.go b/avutil_dict.go index 32cdafb..89848c7 100644 --- a/avutil_dict.go +++ b/avutil_dict.go @@ -35,7 +35,7 @@ func AvDictCount(m *AvDictionary) int32 { } // AvDictSet sets the given entry in *pm, overwriting an existing entry. -func av_dict_set(pm **AvDictionary, key, value string, flags int32) int32 { +func AvDictSet(pm **AvDictionary, key, value string, flags int32) int32 { keyPtr, keyFunc := StringCasting(key) defer keyFunc() valuePtr, valueFunc := StringCasting(value) @@ -71,7 +71,7 @@ func AvDictCopy(dst **AvDictionary, src *AvDictionary, flags int32) int32 { } // AvDictFree frees all the memory allocated for an AVDictionary struct and all keys and values. -func AvDictFree(m *AvDictionary) { +func AvDictFree(m **AvDictionary) { C.av_dict_free((**C.struct_AVDictionary)(unsafe.Pointer(m))) } diff --git a/avutil_error.go b/avutil_error.go index c29b0b5..73feafe 100644 --- a/avutil_error.go +++ b/avutil_error.go @@ -53,7 +53,10 @@ const ( AVERROR_HTTP_NOT_FOUND = int32(C.AVERROR_HTTP_NOT_FOUND) AVERROR_HTTP_OTHER_4XX = int32(C.AVERROR_HTTP_OTHER_4XX) AVERROR_HTTP_SERVER_ERROR = int32(C.AVERROR_HTTP_SERVER_ERROR) - AV_ERROR_MAX_STRING_SIZE = int32(C.AV_ERROR_MAX_STRING_SIZE) +) + +const ( + AV_ERROR_MAX_STRING_SIZE = int32(C.AV_ERROR_MAX_STRING_SIZE) ) // AvStrerror puts a description of the AVERROR code errnum in errbuf. diff --git a/avutil_frame.go b/avutil_frame.go index 7c3f9ab..c12cfe7 100644 --- a/avutil_frame.go +++ b/avutil_frame.go @@ -7,7 +7,7 @@ import "C" import "unsafe" // AvFrameSideDataType -type AvFrameSideDataType int32 +type AvFrameSideDataType = C.enum_AVFrameSideDataType const ( AV_FRAME_DATA_PANSCAN = AvFrameSideDataType(C.AV_FRAME_DATA_PANSCAN) @@ -37,7 +37,7 @@ const ( ) // AvActiveFormatDescription -type AvActiveFormatDescription int32 +type AvActiveFormatDescription = C.enum_AVActiveFormatDescription const ( AV_AFD_SAME = AvActiveFormatDescription(C.AV_AFD_SAME) @@ -127,6 +127,18 @@ func (frame *AvFrame) GetExtendedDataAddr() ***uint8 { return (***uint8)(unsafe.Pointer(&frame.extended_data)) } +// Custom: GetExtendedDataIdx gets `AVFrame.extended_data` index value. +func (frame *AvFrame) GetExtendedDataIdx(idx int) *uint8 { + if frame.extended_data == nil { + return nil + } + return *(**uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(frame.extended_data)) + + uintptr(unsafe.Sizeof(*frame.extended_data))*(uintptr(idx)))) + + // Another way. + // return unsafe.Slice((**uint8)(unsafe.Pointer(frame.extended_data)), idx+1)[idx] +} + // Custom: GetWidth gets `AVFrame.width` value. func (frame *AvFrame) GetWidth() int32 { return (int32)(frame.width) diff --git a/avutil_imgutils.go b/avutil_imgutils.go new file mode 100644 index 0000000..f87e1a4 --- /dev/null +++ b/avutil_imgutils.go @@ -0,0 +1,203 @@ +package ffmpeg + +/* +#include +*/ +import "C" +import ( + "unsafe" +) + +// AvImageFillMaxPixsteps computes the max pixel step for each plane of an image with a +// format described by pixdesc. +func AvImageFillMaxPixsteps(maxPixsteps, maxPixstepComps []int32, pixdesc *AvPixFmtDescriptor) { + if len(maxPixsteps) != 4 { + panic("maxPixsteps need len = 4") + } + if len(maxPixstepComps) != 4 { + panic("maxPixstepComps need len = 4") + } + C.av_image_fill_max_pixsteps((*C.int)(&maxPixsteps[0]), (*C.int)(&maxPixstepComps[0]), + (*C.struct_AVPixFmtDescriptor)(pixdesc)) +} + +// AvImageGetLinesize computes the size of an image line with format pix_fmt and width +// width for the plane plane. +func AvImageGetLinesize(pixFmt AvPixelFormat, width, plane int32) int32 { + return (int32)(C.av_image_get_linesize((C.enum_AVPixelFormat)(pixFmt), + (C.int)(width), (C.int)(plane))) +} + +// AvImageFillLinesizes fills plane linesizes for an image with pixel format pix_fmt and width width. +func AvImageFillLinesizes(linesizes []int32, pixFmt AvPixelFormat, width int32) int32 { + if len(linesizes) != 4 { + panic("linesizes need len = 4") + } + return (int32)(C.av_image_fill_linesizes((*C.int)(&linesizes[0]), + (C.enum_AVPixelFormat)(pixFmt), (C.int)(width))) +} + +// AvImageFillPlaneSizes fills plane sizes for an image with pixel format pix_fmt and height height. +func AvImageFillPlaneSizes(size []uint, pixFmt AvPixelFormat, height int32, linesizes []int) int32 { + if len(size) != 4 { + panic("size need len = 4") + } + if len(linesizes) != 4 { + panic("linesizes need len = 4") + } + return (int32)(C.av_image_fill_plane_sizes((*C.size_t)(unsafe.Pointer(&size[0])), + (C.enum_AVPixelFormat)(pixFmt), (C.int)(height), (*C.ptrdiff_t)(unsafe.Pointer(&linesizes[0])))) +} + +// AvImageFillPointers fills plane data pointers for an image with pixel format pix_fmt and height height. +func AvImageFillPointers(data []*uint8, pixFmt AvPixelFormat, + height int32, ptr *uint8, linesizes []int32) int32 { + if len(data) != 4 { + panic("data need len = 4") + } + if len(linesizes) != 4 { + panic("linesizes need len = 4") + } + return (int32)(C.av_image_fill_pointers((**C.uint8_t)(unsafe.Pointer(&data[0])), + (C.enum_AVPixelFormat)(pixFmt), (C.int)(height), (*C.uint8_t)(ptr), + (*C.int)(&linesizes[0]))) +} + +// AvImageAlloc allocates an image with size w and h and pixel format pix_fmt, and +// fill pointers and linesizes accordingly. +func AvImageAlloc(pointers []*uint8, linesizes []int32, w, h int32, + pixFmt AvPixelFormat, align int32) int32 { + if len(pointers) != 4 { + panic("pointers need len = 4") + } + if len(linesizes) != 4 { + panic("linesizes need len = 4") + } + return (int32)(C.av_image_alloc((**C.uint8_t)(unsafe.Pointer(&pointers[0])), + (*C.int)(&linesizes[0]), + (C.int)(w), (C.int)(h), (C.enum_AVPixelFormat)(pixFmt), (C.int)(align))) +} + +// AvImageCopyPlane copies image plane from src to dst. +func AvImageCopyPlane(dst *uint8, dstLinesize int32, src *uint8, + srcLinesize int32, bytewidth, height int32) { + C.av_image_copy_plane((*C.uint8_t)(dst), (C.int)(dstLinesize), + (*C.uint8_t)(src), (C.int)(srcLinesize), + (C.int)(bytewidth), (C.int)(height)) +} + +// AvImageCopy copies image in src_data to dst_data. +func AvImageCopy(dstData []*uint8, dstLinesizes []int32, srcData []*uint8, srcLinesizes []int32, + pixFmt AvPixelFormat, width, height int32) { + if len(dstData) != 4 { + panic("dstData need len = 4") + } + if len(dstLinesizes) != 4 { + panic("dstLinesizes need len = 4") + } + if len(srcData) != 4 { + panic("srcData need len = 4") + } + if len(srcLinesizes) != 4 { + panic("srcLinesizes need len = 4") + } + C.av_image_copy((**C.uint8_t)(unsafe.Pointer(&dstData[0])), + (*C.int)(&dstLinesizes[0]), + (**C.uint8_t)(unsafe.Pointer(&srcData[0])), + (*C.int)(&srcLinesizes[0]), + (C.enum_AVPixelFormat)(pixFmt), (C.int)(width), (C.int)(height)) +} + +// AvImageCopyUcFrom copies image data located in uncacheable (e.g. GPU mapped) memory. +func AvImageCopyUcFrom(dstData []*uint8, dstLinesizes []int, srcData []*uint8, srcLinesizes []int, + pixFmt AvPixelFormat, width, height int32) { + if len(dstData) != 4 { + panic("dstData need len = 4") + } + if len(dstLinesizes) != 4 { + panic("dstLinesizes need len = 4") + } + if len(srcData) != 4 { + panic("srcData need len = 4") + } + if len(srcLinesizes) != 4 { + panic("srcLinesizes need len = 4") + } + C.av_image_copy_uc_from((**C.uint8_t)(unsafe.Pointer(&dstData[0])), + (*C.ptrdiff_t)(unsafe.Pointer(&dstLinesizes[0])), + (**C.uint8_t)(unsafe.Pointer(&srcData[0])), + (*C.ptrdiff_t)(unsafe.Pointer(&srcLinesizes[0])), + (C.enum_AVPixelFormat)(pixFmt), (C.int)(width), (C.int)(height)) +} + +// AvImageFillArrays setups the data pointers and linesizes based on the specified image +// parameters and the provided array. +func AvImageFillArrays(dstData []*uint8, dstLinesize []int32, src *uint8, + pixFmt AvPixelFormat, width, height, align int32) { + if len(dstData) != 4 { + panic("dstData need len = 4") + } + if len(dstLinesize) != 4 { + panic("dstLinesize need len = 4") + } + C.av_image_fill_arrays((**C.uint8_t)(unsafe.Pointer(&dstData[0])), + (*C.int)(&dstLinesize[0]), + (*C.uint8_t)(src), + (C.enum_AVPixelFormat)(pixFmt), (C.int)(width), (C.int)(height), (C.int)(align)) +} + +// AvImageGetBufferSize Return the size in bytes of the amount of data required to store an +// image with the given parameters. +func AvImageGetBufferSize(pixFmt AvPixelFormat, width, height, align int32) int32 { + return (int32)(C.av_image_get_buffer_size((C.enum_AVPixelFormat)(pixFmt), + (C.int)(width), (C.int)(height), (C.int)(align))) +} + +// av_image_copy_to_buffer +func av_image_copy_to_buffer(dst *uint8, dstSize int32, srcData []*uint8, srcLinesize []int32, + pixFmt AvPixelFormat, width, height, align int32) { + if len(srcData) != 4 { + panic("srcData need len = 4") + } + if len(srcLinesize) != 4 { + panic("srcLinesize need len = 4") + } + C.av_image_copy_to_buffer((*C.uint8_t)(dst), (C.int)(dstSize), + (**C.uint8_t)(unsafe.Pointer(&srcData[0])), (*C.int)(&srcLinesize[0]), + (C.enum_AVPixelFormat)(pixFmt), (C.int)(width), (C.int)(height), (C.int)(align)) +} + +// AvImageCheckSize checks if the given dimension of an image is valid, meaning that all +// bytes of the image can be addressed with a signed int. +func AvImageCheckSize(w, h uint32, logOffset int32, logCtx unsafe.Pointer) int32 { + return (int32)(C.av_image_check_size((C.uint)(w), (C.uint)(h), (C.int)(logOffset), logCtx)) +} + +// AvImageCheckSize2 checks if the given dimension of an image is valid, meaning that all +// bytes of a plane of an image with the specified pix_fmt can be addressed with a signed int. +func AvImageCheckSize2(w, h uint32, maxPixels int64, pixFmt AvPixelFormat, + logOffset int32, logCtx unsafe.Pointer) int32 { + return (int32)(C.av_image_check_size2((C.uint)(w), (C.uint)(h), + (C.int64_t)(maxPixels), (C.enum_AVPixelFormat)(pixFmt), + (C.int)(logOffset), logCtx)) +} + +// AvImageCheckSar checks if the given sample aspect ratio of an image is valid. +func AvImageCheckSar(w, h uint32, sar AvRational) int32 { + return (int32)(C.av_image_check_sar((C.uint)(w), (C.uint)(h), (C.struct_AVRational)(sar))) +} + +// AvImageFillBlack overwrites the image data with black. +func AvImageFillBlack(dstData []*uint8, dstLinesize []int, + pixFmt AvPixelFormat, _range AvColorRange, width, height int32) int32 { + if len(dstData) != 4 { + panic("dstData need len = 4") + } + if len(dstLinesize) != 4 { + panic("dstLinesize need len = 4") + } + return (int32)(C.av_image_fill_black((**C.uint8_t)(unsafe.Pointer(&dstData[0])), + (*C.ptrdiff_t)(unsafe.Pointer(&dstLinesize[0])), + (C.enum_AVPixelFormat)(pixFmt), (C.enum_AVColorRange)(_range), + (C.int)(width), (C.int)(height))) +} diff --git a/avutil_log.go b/avutil_log.go index dc6e19a..e0cc7df 100644 --- a/avutil_log.go +++ b/avutil_log.go @@ -2,11 +2,26 @@ package ffmpeg /* #include + +void av_log_wrap(void *avcl, int level, char *fmt) { + av_log(avcl, level, fmt, NULL); +} + +void av_log_once_wrap(void* avcl, int initial_level, int subsequent_level, int *state, char *fmt) { + av_log_once(avcl, initial_level, subsequent_level, state, fmt, NULL); +} + +typedef void (*av_log_callback_func)(void*, int, const char*, va_list); + */ import "C" +import ( + "fmt" + "unsafe" +) // AvClassCategory -type AvClassCategory int32 +type AvClassCategory = int32 const ( AV_CLASS_CATEGORY_NA = AvClassCategory(C.AV_CLASS_CATEGORY_NA) @@ -44,31 +59,81 @@ func AV_IS_OUTPUT_DEVICE(c AvClassCategory) bool { // AvClass type AvClass C.struct_AVClass -// AvLogLevelType -type AvLogLevelType int32 - const ( - AV_LOG_QUIET = AvLogLevelType(C.AV_LOG_QUIET) - AV_LOG_PANIC = AvLogLevelType(C.AV_LOG_PANIC) - AV_LOG_FATAL = AvLogLevelType(C.AV_LOG_FATAL) - AV_LOG_ERROR = AvLogLevelType(C.AV_LOG_ERROR) - AV_LOG_WARNING = AvLogLevelType(C.AV_LOG_WARNING) - AV_LOG_INFO = AvLogLevelType(C.AV_LOG_INFO) - AV_LOG_VERBOSE = AvLogLevelType(C.AV_LOG_VERBOSE) - AV_LOG_DEBUG = AvLogLevelType(C.AV_LOG_DEBUG) - AV_LOG_TRACE = AvLogLevelType(C.AV_LOG_TRACE) + AV_LOG_QUIET = int32(C.AV_LOG_QUIET) + AV_LOG_PANIC = int32(C.AV_LOG_PANIC) + AV_LOG_FATAL = int32(C.AV_LOG_FATAL) + AV_LOG_ERROR = int32(C.AV_LOG_ERROR) + AV_LOG_WARNING = int32(C.AV_LOG_WARNING) + AV_LOG_INFO = int32(C.AV_LOG_INFO) + AV_LOG_VERBOSE = int32(C.AV_LOG_VERBOSE) + AV_LOG_DEBUG = int32(C.AV_LOG_DEBUG) + AV_LOG_TRACE = int32(C.AV_LOG_TRACE) ) +const ( + AV_LOG_MAX_OFFSET = int32(C.AV_LOG_MAX_OFFSET) +) + +// AV_LOG_C sets additional colors for extended debugging sessions. +func AV_LOG_C(x int32) int32 { + return x << 8 +} + +// AvLog sends the specified message to the log if the level is less than or equal +// to the current av_log_level. By default, all logging messages are sent to stderr. +// This behavior can be altered by setting a different logging callback function. +func AvLog(avcl unsafe.Pointer, level int32, _fmt string, va ...any) { + strPtr, strFunc := StringCasting(fmt.Sprintf(_fmt, va...)) + defer strFunc() + C.av_log_wrap(avcl, (C.int)(level), (*C.char)(strPtr)) +} + +// AvLogOnce sends the specified message to the log once with the initial_level and then with +// the subsequent_level. By default, all logging messages are sent to stderr. +// This behavior can be altered by setting a different logging callback function. +func AvLogOnce(avcl unsafe.Pointer, initialLevel, subsequentLevel int32, state *int32, _fmt string, va ...any) { + fmtPtr, fmtFunc := StringCasting(fmt.Sprintf(_fmt, va...)) + defer fmtFunc() + C.av_log_once_wrap(avcl, (C.int)(initialLevel), (C.int)(subsequentLevel), (*C.int)(state), (*C.char)(fmtPtr)) +} + +// NONEED: av_vlog + +// AvLogGetLevel gets the current log level +func AvLogGetLevel() int32 { + return (int32)(C.av_log_get_level()) +} + // AvLogSetLevel sets the log level -func AvLogSetLevel(level AvLogLevelType) { +func AvLogSetLevel(level int32) { C.av_log_set_level(C.int(level)) } -// AvLogGetLevel gets the current log level -func AvLogGetLevel() AvLogLevelType { - return AvLogLevelType(C.av_log_get_level()) +// typedef void (*av_log_callback_func)(void*, int, const char*, va_list) +type AvLogCallbackFunc C.av_log_callback_func + +// AvLogSetCallback sets the logging callback +func AvLogSetCallback(f AvLogCallbackFunc) { + C.av_log_set_callback(f) } +// NONEED: av_log_default_callback + +// AvDefaultItemName returns the context name +func AvDefaultItemName(ctx unsafe.Pointer) string { + return C.GoString(C.av_default_item_name(ctx)) +} + +// AvDefaultGetCategory +func AvDefaultGetCategory(ptr unsafe.Pointer) AvClassCategory { + return (AvClassCategory)(C.av_default_get_category(ptr)) +} + +// NONEED: av_log_format_line + +// NONEED: av_log_format_line2 + const ( AV_LOG_SKIP_REPEATED = C.AV_LOG_SKIP_REPEATED AV_LOG_PRINT_LEVEL = C.AV_LOG_PRINT_LEVEL diff --git a/avutil_mathematics.go b/avutil_mathematics.go index 0035d57..191a018 100644 --- a/avutil_mathematics.go +++ b/avutil_mathematics.go @@ -5,7 +5,7 @@ package ffmpeg */ import "C" -type AvRounding int32 +type AvRounding = C.enum_AVRounding const ( AV_ROUND_ZERO = AvRounding(C.AV_ROUND_ZERO) diff --git a/avutil_md5.go b/avutil_md5.go new file mode 100644 index 0000000..93312e6 --- /dev/null +++ b/avutil_md5.go @@ -0,0 +1,38 @@ +package ffmpeg + +/* +#include +*/ +import "C" + +var ( + AvMd5Size = C.av_md5_size +) + +// AvMD5 +type AvMD5 C.struct_AVMD5 + +// AvMd5Alloc allocates an AvMD5 context. +func AvMd5Alloc() *AvMD5 { + return (*AvMD5)(C.av_md5_alloc()) +} + +// AvMd5Init initializes MD5 hashing. +func AvMd5Init(ctx *AvMD5) { + C.av_md5_init((*C.struct_AVMD5)(ctx)) +} + +// AvMd5Update updates hash value. +func AvMd5Update(ctx *AvMD5, src *uint8, len int32) { + C.av_md5_update((*C.struct_AVMD5)(ctx), (*C.uint8_t)(src), (C.int)(len)) +} + +// AvMd5Final finishs hashing and output digest value. +func AvMd5Final(ctx *AvMD5, dst *uint8) { + C.av_md5_final((*C.struct_AVMD5)(ctx), (*C.uint8_t)(dst)) +} + +// AvMd5Sum hashes an array of data. +func AvMd5Sum(dst, src *uint8, len int32) { + C.av_md5_sum((*C.uint8_t)(dst), (*C.uint8_t)(src), (C.int)(len)) +} diff --git a/avutil_motion_vector.go b/avutil_motion_vector.go new file mode 100644 index 0000000..1a55790 --- /dev/null +++ b/avutil_motion_vector.go @@ -0,0 +1,173 @@ +package ffmpeg + +/* +#include +*/ +import "C" + +type AvMotionVector C.struct_AVMotionVector + +// Custom: GetSource gets `AVMotionVector.source` value. +func (mv *AvMotionVector) GetSource() int32 { + return (int32)(mv.source) +} + +// Custom: SetSource sets `AVMotionVector.source` value. +func (mv *AvMotionVector) SetSource(v int32) { + mv.source = (C.int32_t)(v) +} + +// Custom: GetSourceAddr gets `AVMotionVector.source` address. +func (mv *AvMotionVector) GetSourceAddr() *int32 { + return (*int32)(&mv.source) +} + +// Custom: GetW gets `AVMotionVector.w` value. +func (mv *AvMotionVector) GetW() uint8 { + return (uint8)(mv.w) +} + +// Custom: SetW sets `AVMotionVector.w` value. +func (mv *AvMotionVector) SetW(v uint8) { + mv.w = (C.uint8_t)(v) +} + +// Custom: GetWAddr gets `AVMotionVector.w` address. +func (mv *AvMotionVector) GetWAddr() *uint8 { + return (*uint8)(&mv.w) +} + +// Custom: GetH gets `AVMotionVector.h` value. +func (mv *AvMotionVector) GetH() uint8 { + return (uint8)(mv.h) +} + +// Custom: SetH sets `AVMotionVector.h` value. +func (mv *AvMotionVector) SetH(v uint8) { + mv.h = (C.uint8_t)(v) +} + +// Custom: GetHAddr gets `AVMotionVector.h` address. +func (mv *AvMotionVector) GetHAddr() *uint8 { + return (*uint8)(&mv.h) +} + +// Custom: GetSrcX gets `AVMotionVector.src_x` value. +func (mv *AvMotionVector) GetSrcX() int16 { + return (int16)(mv.src_x) +} + +// Custom: SetSrcX sets `AVMotionVector.src_x` value. +func (mv *AvMotionVector) SetSrcX(v int16) { + mv.src_x = (C.int16_t)(v) +} + +// Custom: GetSrcXAddr gets `AVMotionVector.src_x` address. +func (mv *AvMotionVector) GetSrcXAddr() *int16 { + return (*int16)(&mv.src_x) +} + +// Custom: GetSrcY gets `AVMotionVector.src_y` value. +func (mv *AvMotionVector) GetSrcY() int16 { + return (int16)(mv.src_y) +} + +// Custom: SetSrcY sets `AVMotionVector.src_y` value. +func (mv *AvMotionVector) SetSrcY(v int16) { + mv.src_y = (C.int16_t)(v) +} + +// Custom: GetSrcYAddr gets `AVMotionVector.src_y` address. +func (mv *AvMotionVector) GetSrcYAddr() *int16 { + return (*int16)(&mv.src_y) +} + +// Custom: GetDstX gets `AVMotionVector.dst_x` value. +func (mv *AvMotionVector) GetDstX() int16 { + return (int16)(mv.dst_x) +} + +// Custom: SetDstX sets `AVMotionVector.dst_x` value. +func (mv *AvMotionVector) SetDstX(v int16) { + mv.dst_x = (C.int16_t)(v) +} + +// Custom: GetDstXAddr gets `AVMotionVector.dst_x` address. +func (mv *AvMotionVector) GetDstXAddr() *int16 { + return (*int16)(&mv.dst_x) +} + +// Custom: GetDstY gets `AVMotionVector.dst_y` value. +func (mv *AvMotionVector) GetDstY() int16 { + return (int16)(mv.dst_y) +} + +// Custom: SetDstY sets `AVMotionVector.dst_y` value. +func (mv *AvMotionVector) SetDstY(v int16) { + mv.dst_y = (C.int16_t)(v) +} + +// Custom: GetDstYAddr gets `AVMotionVector.dst_y` address. +func (mv *AvMotionVector) GetDstYAddr() *int16 { + return (*int16)(&mv.dst_y) +} + +// Custom: GetFlags gets `AVMotionVector.flags` value. +func (mv *AvMotionVector) GetFlags() uint64 { + return (uint64)(mv.flags) +} + +// Custom: SetFlags sets `AVMotionVector.flags` value. +func (mv *AvMotionVector) SetFlags(v uint64) { + mv.flags = (C.uint64_t)(v) +} + +// Custom: GetFlagsAddr gets `AVMotionVector.flags` address. +func (mv *AvMotionVector) GetFlagsAddr() *uint64 { + return (*uint64)(&mv.flags) +} + +// Custom: GetMotionX gets `AVMotionVector.motion_x` value. +func (mv *AvMotionVector) GetMotionX() int32 { + return (int32)(mv.motion_x) +} + +// Custom: SetMotionX sets `AVMotionVector.motion_x` value. +func (mv *AvMotionVector) SetMotionX(v int32) { + mv.motion_x = (C.int32_t)(v) +} + +// Custom: GetMotionXAddr gets `AVMotionVector.motion_x` address. +func (mv *AvMotionVector) GetMotionXAddr() *int32 { + return (*int32)(&mv.motion_x) +} + +// Custom: GetMotionY gets `AVMotionVector.motion_y` value. +func (mv *AvMotionVector) GetMotionY() int32 { + return (int32)(mv.motion_y) +} + +// Custom: SetMotionY sets `AVMotionVector.motion_y` value. +func (mv *AvMotionVector) SetMotionY(v int32) { + mv.motion_y = (C.int32_t)(v) +} + +// Custom: GetMotionYAddr gets `AVMotionVector.motion_y` address. +func (mv *AvMotionVector) GetMotionYAddr() *int32 { + return (*int32)(&mv.motion_y) +} + +// Custom: GetMotionScale gets `AVMotionVector.motion_scale` value. +func (mv *AvMotionVector) GetMotionScale() uint16 { + return (uint16)(mv.motion_scale) +} + +// Custom: SetMotionScale sets `AVMotionVector.motion_scale` value. +func (mv *AvMotionVector) SetMotionScale(v uint16) { + mv.motion_scale = (C.uint16_t)(v) +} + +// Custom: GetMotionScaleAddr gets `AVMotionVector.motion_scale` address. +func (mv *AvMotionVector) GetMotionScaleAddr() *uint16 { + return (*uint16)(&mv.motion_scale) +} diff --git a/avutil_murmur3.go b/avutil_murmur3.go new file mode 100644 index 0000000..3703cf1 --- /dev/null +++ b/avutil_murmur3.go @@ -0,0 +1,36 @@ +package ffmpeg + +/* +#include +*/ +import "C" + +type AvMurMur3 C.struct_AVMurMur3 + +// AvMurmur3Alloc allocates an AVMurMur3 hash context. +func AvMurmur3Alloc() *AvMurMur3 { + return (*AvMurMur3)(C.av_murmur3_alloc()) +} + +// AvMurmur3InitSeeded initializes or reinitializes an AvMurMur3 hash context with a seed. +func AvMurmur3InitSeeded(c *AvMurMur3, seed uint64) { + C.av_murmur3_init_seeded((*C.struct_AVMurMur3)(c), (C.uint64_t)(seed)) +} + +// AvMurmur3Init initializes or reinitializes an AVMurMur3 hash context. +func AvMurmur3Init(c *AvMurMur3) { + C.av_murmur3_init((*C.struct_AVMurMur3)(c)) +} + +// AvMurmur3Update updates hash context with new data. +func AvMurmur3Update(c *AvMurMur3, src *uint8, len int32) { + C.av_murmur3_update((*C.struct_AVMurMur3)(c), (*C.uint8_t)(src), (C.int)(len)) +} + +// av_murmur3_final +func av_murmur3_final(c *AvMurMur3, dst []uint8) { + if len(dst) != 16 { + panic("dst need len = 16") + } + C.av_murmur3_final((*C.struct_AVMurMur3)(c), (*C.uint8_t)(&dst[0])) +} diff --git a/avutil_opt.go b/avutil_opt.go index 0870fd1..878c7ac 100644 --- a/avutil_opt.go +++ b/avutil_opt.go @@ -2,32 +2,42 @@ package ffmpeg /* #include + +int av_opt_set_int_list_wrap(void *obj, const char *name, void *val, uint64_t term, int flags, int size) { + if (av_int_list_length(val, term) > INT_MAX / size) { + return AVERROR(EINVAL); + } + return av_opt_set_bin(obj, name, (const uint8_t *)val, av_int_list_length(val, term) * size , flags); +} */ import "C" -import "unsafe" +import ( + "unsafe" +) -type AvOptionType int32 +// AvOptionType +type AvOptionType = int32 const ( - AV_OPT_TYPE_FLAGS = int32(C.AV_OPT_TYPE_FLAGS) - AV_OPT_TYPE_INT = int32(C.AV_OPT_TYPE_INT) - AV_OPT_TYPE_INT64 = int32(C.AV_OPT_TYPE_INT64) - AV_OPT_TYPE_DOUBLE = int32(C.AV_OPT_TYPE_DOUBLE) - AV_OPT_TYPE_FLOAT = int32(C.AV_OPT_TYPE_FLOAT) - AV_OPT_TYPE_STRING = int32(C.AV_OPT_TYPE_STRING) - AV_OPT_TYPE_RATIONAL = int32(C.AV_OPT_TYPE_RATIONAL) - AV_OPT_TYPE_BINARY = int32(C.AV_OPT_TYPE_BINARY) - AV_OPT_TYPE_DICT = int32(C.AV_OPT_TYPE_DICT) - AV_OPT_TYPE_UINT64 = int32(C.AV_OPT_TYPE_UINT64) - AV_OPT_TYPE_CONST = int32(C.AV_OPT_TYPE_CONST) - AV_OPT_TYPE_IMAGE_SIZE = int32(C.AV_OPT_TYPE_IMAGE_SIZE) - AV_OPT_TYPE_PIXEL_FMT = int32(C.AV_OPT_TYPE_PIXEL_FMT) - AV_OPT_TYPE_SAMPLE_FMT = int32(C.AV_OPT_TYPE_SAMPLE_FMT) - AV_OPT_TYPE_VIDEO_RATE = int32(C.AV_OPT_TYPE_VIDEO_RATE) - AV_OPT_TYPE_DURATION = int32(C.AV_OPT_TYPE_DURATION) - AV_OPT_TYPE_COLOR = int32(C.AV_OPT_TYPE_COLOR) - AV_OPT_TYPE_CHANNEL_LAYOUT = int32(C.AV_OPT_TYPE_CHANNEL_LAYOUT) - AV_OPT_TYPE_BOOL = int32(C.AV_OPT_TYPE_BOOL) + AV_OPT_TYPE_FLAGS = AvOptionType(C.AV_OPT_TYPE_FLAGS) + AV_OPT_TYPE_INT = AvOptionType(C.AV_OPT_TYPE_INT) + AV_OPT_TYPE_INT64 = AvOptionType(C.AV_OPT_TYPE_INT64) + AV_OPT_TYPE_DOUBLE = AvOptionType(C.AV_OPT_TYPE_DOUBLE) + AV_OPT_TYPE_FLOAT = AvOptionType(C.AV_OPT_TYPE_FLOAT) + AV_OPT_TYPE_STRING = AvOptionType(C.AV_OPT_TYPE_STRING) + AV_OPT_TYPE_RATIONAL = AvOptionType(C.AV_OPT_TYPE_RATIONAL) + AV_OPT_TYPE_BINARY = AvOptionType(C.AV_OPT_TYPE_BINARY) + AV_OPT_TYPE_DICT = AvOptionType(C.AV_OPT_TYPE_DICT) + AV_OPT_TYPE_UINT64 = AvOptionType(C.AV_OPT_TYPE_UINT64) + AV_OPT_TYPE_CONST = AvOptionType(C.AV_OPT_TYPE_CONST) + AV_OPT_TYPE_IMAGE_SIZE = AvOptionType(C.AV_OPT_TYPE_IMAGE_SIZE) + AV_OPT_TYPE_PIXEL_FMT = AvOptionType(C.AV_OPT_TYPE_PIXEL_FMT) + AV_OPT_TYPE_SAMPLE_FMT = AvOptionType(C.AV_OPT_TYPE_SAMPLE_FMT) + AV_OPT_TYPE_VIDEO_RATE = AvOptionType(C.AV_OPT_TYPE_VIDEO_RATE) + AV_OPT_TYPE_DURATION = AvOptionType(C.AV_OPT_TYPE_DURATION) + AV_OPT_TYPE_COLOR = AvOptionType(C.AV_OPT_TYPE_COLOR) + AV_OPT_TYPE_CHANNEL_LAYOUT = AvOptionType(C.AV_OPT_TYPE_CHANNEL_LAYOUT) + AV_OPT_TYPE_BOOL = AvOptionType(C.AV_OPT_TYPE_BOOL) ) type AvOption C.struct_AVOption @@ -82,7 +92,7 @@ func AvSetOptionsString(ctx unsafe.Pointer, opts, keyValSep, pairsSep string) in (*C.char)(keyValSepPtr), (*C.char)(pairsSepPtr))) } -// TODO. av_opt_set_from_string +// NONEED: av_opt_set_from_string // AvOptFree frees all allocated objects in obj. func AvOptFree(obj unsafe.Pointer) { @@ -109,7 +119,7 @@ func AvOptSetDict2(obj unsafe.Pointer, options **AvDictionary, searchFlags int32 (C.int)(searchFlags))) } -// TODO. av_opt_get_key_value +// NONEED: av_opt_get_key_value const ( AV_OPT_FLAG_IMPLICIT_KEY = int32(C.AV_OPT_FLAG_IMPLICIT_KEY) @@ -171,7 +181,8 @@ func AvOptFind(obj unsafe.Pointer, name, unit string, optFlags, searchFlags int3 defer nameFunc() unitPtr, unitFunc := StringCasting(unit) defer unitFunc() - return (*AvOption)(C.av_opt_find(obj, namePtr, unitPtr, (C.int)(optFlags), (C.int)(searchFlags))) + return (*AvOption)(C.av_opt_find(obj, (*C.char)(namePtr), (*C.char)(unitPtr), + (C.int)(optFlags), (C.int)(searchFlags))) } // AvOptFind2 looks for an option in an object. Consider only options which @@ -182,7 +193,7 @@ func AvOptFind2(obj unsafe.Pointer, name, unit string, optFlags, searchFlags int defer nameFunc() unitPtr, unitFunc := StringCasting(unit) defer unitFunc() - return (*AvOption)(C.av_opt_find2(obj, namePtr, unitPtr, + return (*AvOption)(C.av_opt_find2(obj, (*C.char)(namePtr), (*C.char)(unitPtr), (C.int)(optFlags), (C.int)(searchFlags), targetObj)) } @@ -286,7 +297,15 @@ func AvOptSetDictVal(obj unsafe.Pointer, name string, val *AvDictionary, searchF return (int32)(C.av_opt_set_dict_val(obj, (*C.char)(namePtr), (*C.struct_AVDictionary)(val), (C.int)(searchFlags))) } -// TODO. av_opt_set_int_list +// AvOptSetIntList sets a binary option to an integer list. +func av_opt_set_int_list[T HelperInteger](obj unsafe.Pointer, name string, + val *T, term uint64, flags int32) int32 { + namePtr, nameFunc := StringCasting(name) + defer nameFunc() + size := (int32)(unsafe.Sizeof(*val)) + return (int32)(C.av_opt_set_int_list_wrap(obj, (*C.char)(namePtr), + unsafe.Pointer(val), (C.uint64_t)(term), (C.int)(flags), (C.int)(size))) +} // AvOptGet func AvOptGet(obj unsafe.Pointer, name string, searchFlags int32, outVal **uint8) int32 { diff --git a/avutil_parseutils.go b/avutil_parseutils.go new file mode 100644 index 0000000..c9cd1f0 --- /dev/null +++ b/avutil_parseutils.go @@ -0,0 +1,72 @@ +package ffmpeg + +/* +#include +*/ +import "C" +import "unsafe" + +// AvParseRatio parses str and store the parsed ratio in q. +func AvParseRatio(q *AvRational, str string, max, logOffset int32, logCtx unsafe.Pointer) int32 { + strPtr, strFunc := StringCasting(str) + defer strFunc() + return (int32)(C.av_parse_ratio((*C.struct_AVRational)(q), + (*C.char)(strPtr), (C.int)(max), (C.int)(logOffset), logCtx)) +} + +// AvParseRatioQuiet +func AvParseRatioQuiet(q *AvRational, str string, max int32) int32 { + strPtr, strFunc := StringCasting(str) + defer strFunc() + return (int32)(C.av_parse_ratio((*C.struct_AVRational)(q), + (*C.char)(strPtr), (C.int)(max), (C.int)(AV_LOG_MAX_OFFSET), nil)) +} + +// AvParseVideoSize parses str and put in width_ptr and height_ptr the detected values. +func AvParseVideoSize(widthPtr, heightPtr *int32, str string) int32 { + strPtr, strFunc := StringCasting(str) + defer strFunc() + return (int32)(C.av_parse_video_size((*C.int)(widthPtr), (*C.int)(heightPtr), (*C.char)(strPtr))) +} + +// AvParseVideoRate parses str and store the detected values in *rate. +func AvParseVideoRate(rate *AvRational, str string) int32 { + strPtr, strFunc := StringCasting(str) + defer strFunc() + return (int32)(C.av_parse_video_rate((*C.struct_AVRational)(rate), (*C.char)(strPtr))) +} + +// AvParseColor puts the RGBA values that correspond to color_string in rgba_color. +func AvParseColor(rgbaColor *uint8, colorString string, slen int32, logCtx unsafe.Pointer) int32 { + strPtr, strFunc := StringCasting(colorString) + defer strFunc() + return (int32)(C.av_parse_color((*C.uint8_t)(rgbaColor), (*C.char)(strPtr), (C.int)(slen), logCtx)) +} + +// AvGetKnownColorName gets the name of a color from the internal table of hard-coded named colors. +func AvGetKnownColorName(colorIdx int32, rgb **uint8) string { + return C.GoString(C.av_get_known_color_name((C.int)(colorIdx), (**C.uint8_t)(unsafe.Pointer(rgb)))) +} + +// AvParseTime parse timestr and return in *time a corresponding number of microseconds. +func AvParseTime(timeval *int64, timestr string, duration int32) int32 { + strPtr, strFunc := StringCasting(timestr) + defer strFunc() + return (int32)(C.av_parse_time((*C.int64_t)(timeval), (*C.char)(strPtr), (C.int)(duration))) +} + +// AvFindInfoTag attempts to find a specific tag in a URL. +func AvFindInfoTag(tag1, info string) (val string, ret int32) { + tag1Ptr, tag1Func := StringCasting(tag1) + defer tag1Func() + infoPtr, infoFunc := StringCasting(info) + defer infoFunc() + infoBuf := make([]C.char, len(info)) + ret = (int32)(C.av_find_info_tag(&infoBuf[0], (C.int)(len(info)), + (*C.char)(tag1Ptr), (*C.char)(infoPtr))) + return C.GoString(&infoBuf[0]), ret +} + +// NONEED: av_small_strptime + +// NONEED: av_timegm diff --git a/avutil_pixdesc.go b/avutil_pixdesc.go index c8f0c5b..2b8bc9f 100644 --- a/avutil_pixdesc.go +++ b/avutil_pixdesc.go @@ -145,8 +145,14 @@ func AvGetPixFmtString(buf *int8, bufSize int32, pixFmt AvPixelFormat) string { // AvReadImageLine2 reads a line from an image, and write the values of the // pixel format component c to dst. -func AvReadImageLine2(dst unsafe.Pointer, data [4]*uint8, linesize [4]int, +func AvReadImageLine2(dst unsafe.Pointer, data []*uint8, linesize []int, desc *AvPixFmtDescriptor, x, y, c, w, readPalComponent, dstElementSize int32) { + if len(data) != 4 { + panic("data need len = 4") + } + if len(linesize) != 4 { + panic("linesize need len = 4") + } C.av_read_image_line2(dst, (**C.uint8_t)(unsafe.Pointer(&data[0])), (*C.int)(unsafe.Pointer(&linesize[0])), @@ -157,8 +163,14 @@ func AvReadImageLine2(dst unsafe.Pointer, data [4]*uint8, linesize [4]int, // AvReadImageLine reads a line from an image, and write the values of the // pixel format component c to dst. -func AvReadImageLine(dst *uint16, data [4]*uint8, linesize [4]int, +func AvReadImageLine(dst *uint16, data []*uint8, linesize []int, desc *AvPixFmtDescriptor, x, y, c, w, readPalComponent int32) { + if len(data) != 4 { + panic("data need len = 4") + } + if len(linesize) != 4 { + panic("linesize need len = 4") + } C.av_read_image_line((*C.uint16_t)(dst), (**C.uint8_t)(unsafe.Pointer(&data[0])), (*C.int)(unsafe.Pointer(&linesize[0])), @@ -168,8 +180,14 @@ func AvReadImageLine(dst *uint16, data [4]*uint8, linesize [4]int, } // AvWriteImageLine2 writes the values from src to the pixel format component c of an image line. -func AvWriteImageLine2(src unsafe.Pointer, data [4]*uint8, linesize [4]int, +func AvWriteImageLine2(src unsafe.Pointer, data []*uint8, linesize []int, desc *AvPixFmtDescriptor, x, y, c, w, srcElementSize int32) { + if len(data) != 4 { + panic("data need len = 4") + } + if len(linesize) != 4 { + panic("linesize need len = 4") + } C.av_write_image_line2(src, (**C.uint8_t)(unsafe.Pointer(&data[0])), (*C.int)(unsafe.Pointer(&linesize[0])), @@ -179,8 +197,14 @@ func AvWriteImageLine2(src unsafe.Pointer, data [4]*uint8, linesize [4]int, } // AvWriteImageLine writes the values from src to the pixel format component c of an image line. -func AvWriteImageLine(src *uint16, data [4]*uint8, linesize [4]int, +func AvWriteImageLine(src *uint16, data []*uint8, linesize []int, desc *AvPixFmtDescriptor, x, y, c, w int32) { + if len(data) != 4 { + panic("data need len = 4") + } + if len(linesize) != 4 { + panic("linesize need len = 4") + } C.av_write_image_line((*C.uint16_t)(src), (**C.uint8_t)(unsafe.Pointer(&data[0])), (*C.int)(unsafe.Pointer(&linesize[0])), diff --git a/avutil_pixelutils.go b/avutil_pixelutils.go new file mode 100644 index 0000000..bdf43fb --- /dev/null +++ b/avutil_pixelutils.go @@ -0,0 +1,17 @@ +package ffmpeg + +/* +#include + +*/ +import "C" +import "unsafe" + +type AvPixelutilsSadFn C.av_pixelutils_sad_fn + +// AvPixelutilsGetSadFn gets a potentially optimized pointer to a Sum-of-absolute-differences +// function (see the av_pixelutils_sad_fn prototype). +func AvPixelutilsGetSadFn(wBits, hBits, aligned int32, logCtx unsafe.Pointer) AvPixelutilsSadFn { + return (AvPixelutilsSadFn)(C.av_pixelutils_get_sad_fn((C.int)(wBits), (C.int)(hBits), + (C.int)(aligned), logCtx)) +} diff --git a/avutil_pixfmt.go b/avutil_pixfmt.go index 9571800..cde5c45 100644 --- a/avutil_pixfmt.go +++ b/avutil_pixfmt.go @@ -6,7 +6,7 @@ package ffmpeg import "C" // Pixel format. -type AvPixelFormat int32 +type AvPixelFormat = C.enum_AVPixelFormat const ( AV_PIX_FMT_NONE = AvPixelFormat(C.AV_PIX_FMT_NONE) @@ -343,7 +343,7 @@ const ( ) // Chromaticity coordinates of the source primaries. -type AvColorPrimaries int32 +type AvColorPrimaries = C.enum_AVColorPrimaries const ( AVCOL_PRI_RESERVED0 = AvColorPrimaries(C.AVCOL_PRI_RESERVED0) @@ -367,7 +367,7 @@ const ( ) // Color Transfer Characteristic. -type AvColorTransferCharacteristic int32 +type AvColorTransferCharacteristic = C.enum_AVColorTransferCharacteristic const ( AVCOL_TRC_RESERVED0 = AvColorTransferCharacteristic(C.AVCOL_TRC_RESERVED0) @@ -395,7 +395,7 @@ const ( ) // AvColorSpace -type AvColorSpace int32 +type AvColorSpace = C.enum_AVColorSpace const ( AVCOL_SPC_RGB = AvColorSpace(C.AVCOL_SPC_RGB) @@ -418,7 +418,7 @@ const ( ) // AvColorRange -type AvColorRange int32 +type AvColorRange = C.enum_AVColorRange const ( AVCOL_RANGE_UNSPECIFIED = AvColorRange(C.AVCOL_RANGE_UNSPECIFIED) @@ -428,7 +428,7 @@ const ( ) // AvChromaLocation -type AvChromaLocation int32 +type AvChromaLocation = C.enum_AVChromaLocation const ( AVCHROMA_LOC_UNSPECIFIED = AvChromaLocation(C.AVCHROMA_LOC_UNSPECIFIED) diff --git a/avutil_samplefmt.go b/avutil_samplefmt.go index 6efd579..5ac5660 100644 --- a/avutil_samplefmt.go +++ b/avutil_samplefmt.go @@ -7,7 +7,7 @@ import "C" import "unsafe" // AvSampleFormat -type AvSampleFormat int32 +type AvSampleFormat = C.enum_AVSampleFormat const ( AV_SAMPLE_FMT_NONE = AvSampleFormat(C.AV_SAMPLE_FMT_NONE) diff --git a/examples/encode-audio/main.go b/examples/encode-audio/main.go index 361bb28..6e7b2c1 100644 --- a/examples/encode-audio/main.go +++ b/examples/encode-audio/main.go @@ -141,7 +141,7 @@ func main() { } frame.SetNbSamples(avctx.GetFrameSize()) - frame.SetFormat(int32(avctx.GetSampleFmt())) + frame.SetFormat(avctx.GetSampleFmt()) frame.SetChannelLayout(avctx.GetChannelLayout()) // allocate the data buffers diff --git a/examples/encode-video/main.go b/examples/encode-video/main.go index 65dff94..f447657 100644 --- a/examples/encode-video/main.go +++ b/examples/encode-video/main.go @@ -101,7 +101,7 @@ func main() { fmt.Fprintf(os.Stderr, "Could not allocate video frame\n") os.Exit(1) } - frame.SetFormat(int32(avctx.GetPixFmt())) + frame.SetFormat(avctx.GetPixFmt()) frame.SetWidth(avctx.GetWidth()) frame.SetHeight(avctx.GetHeight()) diff --git a/examples/filter-audio/main.go b/examples/filter-audio/main.go index 7905807..5698080 100644 --- a/examples/filter-audio/main.go +++ b/examples/filter-audio/main.go @@ -1,5 +1,296 @@ package main -func main() { +import ( + "fmt" + "math" + "os" + "strconv" + "syscall" + "unsafe" + "github.com/qrtc/ffmpeg-dev-go" +) + +const ( + INPUT_SAMPLERATE = 48000 + INPUT_FORMAT = ffmpeg.AV_SAMPLE_FMT_FLTP + INPUT_CHANNEL_LAYOUT = ffmpeg.AV_CH_LAYOUT_5POINT0 + VOLUME_VAL = 0.90 + FRAME_SIZE = 1024 +) + +func initFilterGraph() (graph *ffmpeg.AvFilterGraph, src *ffmpeg.AvFilterContext, sink *ffmpeg.AvFilterContext, ret int32) { + var filterGraph *ffmpeg.AvFilterGraph + var abufferCtx *ffmpeg.AvFilterContext + var abuffer *ffmpeg.AvFilter + var volumeCtx *ffmpeg.AvFilterContext + var volume *ffmpeg.AvFilter + var aformatCtx *ffmpeg.AvFilterContext + var aformat *ffmpeg.AvFilter + var abuffersinkCtx *ffmpeg.AvFilterContext + var abuffersink *ffmpeg.AvFilter + var optionsDict *ffmpeg.AvDictionary + + // Create a new filtergraph, which will contain all the filters. + if filterGraph = ffmpeg.AvFilterGraphAlloc(); filterGraph == nil { + fmt.Fprintf(os.Stderr, "Unable to create filter graph.\n") + return nil, nil, nil, ffmpeg.AVERROR(int32(syscall.ENOMEM)) + } + + // Create the abuffer filter; + // it will be used for feeding the data into the graph. + if abuffer = ffmpeg.AvFilterGetByName("abuffer"); abuffer == nil { + fmt.Fprintf(os.Stderr, "Could not find the abuffer filter.\n") + return nil, nil, nil, ffmpeg.AVERROR_FILTER_NOT_FOUND + } + + if abufferCtx = ffmpeg.AvFilterGraphAllocFilter(filterGraph, abuffer, "src"); abufferCtx == nil { + fmt.Fprintf(os.Stderr, "Could not allocate the abuffer instance.\n") + return nil, nil, nil, ffmpeg.AVERROR(int32(syscall.ENOMEM)) + } + + ffmpeg.AvOptSet(unsafe.Pointer(abufferCtx), "channel_layout", + ffmpeg.AvGetChannelLayoutString(0, INPUT_CHANNEL_LAYOUT), ffmpeg.AV_OPT_SEARCH_CHILDREN) + ffmpeg.AvOptSet(unsafe.Pointer(abufferCtx), "sample_fmt", + ffmpeg.AvGetSampleFmtName(INPUT_FORMAT), ffmpeg.AV_OPT_SEARCH_CHILDREN) + ffmpeg.AvOptSetQ(unsafe.Pointer(abufferCtx), "time_base", + ffmpeg.AvMakeQ(1, INPUT_SAMPLERATE), ffmpeg.AV_OPT_SEARCH_CHILDREN) + ffmpeg.AvOptSetInt(unsafe.Pointer(abufferCtx), "sample_rate", + INPUT_SAMPLERATE, ffmpeg.AV_OPT_SEARCH_CHILDREN) + + // Now initialize the filter; we pass NULL options, since we have already set all the options above. + if ret = ffmpeg.AvFilterInitStr(abufferCtx, ""); ret < 0 { + fmt.Fprintf(os.Stderr, "Could not initialize the abuffer filter.\n") + return nil, nil, nil, ret + } + + // Create volume filter. + if volume = ffmpeg.AvFilterGetByName("volume"); volume == nil { + fmt.Fprintf(os.Stderr, "Could not find the volume filter.\n") + return nil, nil, nil, ffmpeg.AVERROR_FILTER_NOT_FOUND + } + + if volumeCtx = ffmpeg.AvFilterGraphAllocFilter(filterGraph, volume, "volume"); volumeCtx == nil { + fmt.Fprintf(os.Stderr, "Could not allocate the volume instance.\n") + return nil, nil, nil, ffmpeg.AVERROR(int32(syscall.ENOMEM)) + } + + // A different way of passing the options is as key/value pairs in a + // dictionary. + ffmpeg.AvDictSet(&optionsDict, "volume", fmt.Sprintf("%f", VOLUME_VAL), 0) + ret = ffmpeg.AvFilterInitDict(volumeCtx, &optionsDict) + ffmpeg.AvDictFree(&optionsDict) + if ret < 0 { + fmt.Fprintf(os.Stderr, "Could not initialize the volume filter.\n") + return nil, nil, nil, ret + } + + // Create the aformat filter; + // it ensures that the output is of the format we want. + if aformat = ffmpeg.AvFilterGetByName("aformat"); aformat == nil { + fmt.Fprintf(os.Stderr, "Could not find the aformat filter.\n") + return nil, nil, nil, ffmpeg.AVERROR_FILTER_NOT_FOUND + } + + if aformatCtx = ffmpeg.AvFilterGraphAllocFilter(filterGraph, aformat, "aformat"); aformatCtx == nil { + fmt.Fprintf(os.Stderr, "Could not allocate the aformat instance.\n") + return nil, nil, nil, ffmpeg.AVERROR(int32(syscall.ENOMEM)) + } + + // A third way of passing the options is in a string of the form + // key1=value1:key2=value2.... + optionsStr := fmt.Sprintf("sample_fmts=%s:sample_rates=%d:channel_layouts=0x%d", + ffmpeg.AvGetSampleFmtName(ffmpeg.AV_SAMPLE_FMT_S16), 44100, (uint64)(ffmpeg.AV_CH_LAYOUT_STEREO)) + if ret = ffmpeg.AvFilterInitStr(aformatCtx, optionsStr); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Could not initialize the aformat filter.\n") + return nil, nil, nil, ret + } + + // Finally create the abuffersink filter; + // it will be used to get the filtered data out of the graph. + if abuffersink = ffmpeg.AvFilterGetByName("abuffersink"); abuffersink == nil { + fmt.Fprintf(os.Stderr, "Could not find the abuffersink filter.\n") + return nil, nil, nil, ffmpeg.AVERROR_FILTER_NOT_FOUND + } + + if abuffersinkCtx = ffmpeg.AvFilterGraphAllocFilter(filterGraph, abuffersink, "sink"); abuffersinkCtx == nil { + fmt.Fprintf(os.Stderr, "Could not allocate the abuffersink instance.\n") + return nil, nil, nil, ffmpeg.AVERROR(int32(syscall.ENOMEM)) + } + + // This filter takes no options. + if ret = ffmpeg.AvFilterInitStr(abuffersinkCtx, ""); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Could not initialize the abuffersink instance.\n") + return nil, nil, nil, ret + } + + // Connect the filters; + // in this simple case the filters just form a linear chain. + ret = ffmpeg.AvFilterLink2(abufferCtx, 0, volumeCtx, 0) + if ret >= 0 { + ret = ffmpeg.AvFilterLink2(volumeCtx, 0, aformatCtx, 0) + } + if ret >= 0 { + ret = ffmpeg.AvFilterLink2(aformatCtx, 0, abuffersinkCtx, 0) + } + if ret < 0 { + fmt.Fprintf(os.Stderr, "Error connecting filters\n") + return nil, nil, nil, ret + } + + // Configure the graph. + if ret = ffmpeg.AvFilterGraphConfig(filterGraph, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Error configuring the filter graph\n") + return nil, nil, nil, ret + } + + return filterGraph, abufferCtx, abuffersinkCtx, 0 +} + +// Do something useful with the filtered data: this simple +// example just prints the MD5 checksum of each plane to stdout. +func processOutput(md5 *ffmpeg.AvMD5, frame *ffmpeg.AvFrame) int32 { + planar := ffmpeg.AvSampleFmtIsPlanar(frame.GetFormat()) + channels := ffmpeg.AvGetChannelLayoutNbChannels(frame.GetChannelLayout()) + planes := channels + if planar == 0 { + planes = 1 + } + bps := ffmpeg.AvGetBytesPerSample(frame.GetFormat()) + planeSize := bps * frame.GetNbSamples() + if planar == 0 { + planeSize *= channels + } + + for i := 0; i < int(planes); i++ { + var checksum [16]uint8 + + ffmpeg.AvMd5Init(md5) + ffmpeg.AvMd5Sum(&checksum[0], frame.GetExtendedDataIdx(i), planeSize) + + fmt.Fprintf(os.Stdout, "plane %d: 0x", i) + for j := 0; j < len(checksum); j++ { + fmt.Fprintf(os.Stdout, "%02X", checksum[j]) + } + fmt.Fprintf(os.Stdout, "\n") + } + fmt.Fprintf(os.Stdout, "\n") + + return 0 +} + +// Construct a frame of audio data to be filtered; +// this simple example just synthesizes a sine wave. +func getInput(frame *ffmpeg.AvFrame, frameNum int32) int32 { + // Set up the frame properties and allocate the buffer for the data. + frame.SetSampleRate(INPUT_SAMPLERATE) + frame.SetFormat(INPUT_FORMAT) + frame.SetChannelLayout(INPUT_CHANNEL_LAYOUT) + frame.SetNbSamples(FRAME_SIZE) + frame.SetPts(int64(frameNum) * FRAME_SIZE) + + if ret := ffmpeg.AvFrameGetBuffer(frame, 0); ret < 0 { + return ret + } + + // Fill the data for each channel. + for i := 0; i < 5; i++ { + dataLen := int(frame.GetNbSamples()) + data := (*float32)(unsafe.Pointer(frame.GetExtendedDataIdx(i))) + dataSlice := unsafe.Slice(data, dataLen) + + for j := 0; j < dataLen; j++ { + dataSlice[j] = (float32)(math.Sin(2 * math.Pi * (float64)(int(frameNum)+j) * (float64)(i+1) / FRAME_SIZE)) + } + } + + return 0 +} + +func main() { + var md5 *ffmpeg.AvMD5 + var graph *ffmpeg.AvFilterGraph + var src, sink *ffmpeg.AvFilterContext + var frame *ffmpeg.AvFrame + var ret int32 + + if len(os.Args) < 2 { + fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0]) + os.Exit(1) + } + + duration, err := strconv.ParseFloat(os.Args[1], 32) + if err != nil { + fmt.Fprintf(os.Stderr, "Invalid duration: %s\n", os.Args[1]) + os.Exit(1) + } + nbFrames := (int32)(duration * INPUT_SAMPLERATE / FRAME_SIZE) + if nbFrames <= 0 { + fmt.Fprintf(os.Stderr, "Invalid duration: %s\n", os.Args[1]) + os.Exit(1) + } + + // Allocate the frame we will be using to store the data. + if frame = ffmpeg.AvFrameAlloc(); frame == nil { + fmt.Fprintf(os.Stderr, "Error allocating the frame\n") + os.Exit(1) + } + + if md5 = ffmpeg.AvMd5Alloc(); md5 == nil { + fmt.Fprintf(os.Stderr, "Error allocating the MD5 context\n") + os.Exit(1) + } + + // Set up the filtergraph. + if graph, src, sink, ret = initFilterGraph(); ret < 0 { + fmt.Fprintf(os.Stderr, "Unable to init filter graph:") + goto fail + } + + // the main filtering loop + for i := int32(0); i < nbFrames; i++ { + // get an input frame to be filtered + if ret = getInput(frame, i); ret < 0 { + fmt.Fprintf(os.Stderr, "Error generating input frame:") + goto fail + } + + // Send the frame to the input of the filtergraph. + if ret = ffmpeg.AvBuffersrcAddFrame(src, frame); ret < 0 { + ffmpeg.AvFrameUnref(frame) + fmt.Fprintf(os.Stderr, "Error submitting the frame to the filtergraph:") + goto fail + } + + // Get all the filtered output that is available. + for ffmpeg.AvBuffersinkGetFrame(sink, frame) >= 0 { + // now do something with our filtered frame + if ret = processOutput(md5, frame); ret < 0 { + fmt.Fprintf(os.Stderr, "Error processing the filtered frame:") + goto fail + } + ffmpeg.AvFrameUnref(frame) + } + + if ret == ffmpeg.AVERROR(int32(syscall.EAGAIN)) { + // Need to feed more frames in. + continue + } else if ret == ffmpeg.AVERROR_EOF { + // Nothing more to do, finish. + break + } else if ret < 0 { + // An error occurred. + fmt.Fprintf(os.Stderr, "Error filtering the data:") + goto fail + } + } + + ffmpeg.AvFilterGraphFree(&graph) + ffmpeg.AvFrameFree(&frame) + ffmpeg.AvFreep(unsafe.Pointer(&md5)) + return + +fail: + fmt.Fprintf(os.Stderr, "%s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) } diff --git a/examples/scaling-video/main.go b/examples/scaling-video/main.go index 7905807..8c63a2a 100644 --- a/examples/scaling-video/main.go +++ b/examples/scaling-video/main.go @@ -1,5 +1,115 @@ package main -func main() { +import ( + "fmt" + "os" + "syscall" + "unsafe" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +func fillYuvImage(data [4]*uint8, linesize [4]int32, width, height, frameIndex int32) { + // Y + data0 := unsafe.Slice(data[0], height*linesize[0]+width) + for y := int32(0); y < height; y++ { + for x := int32(0); x < width; x++ { + data0[y*linesize[0]+x] = (uint8)(x + y + frameIndex*3) + } + } + + // Cb and Cr + data1 := unsafe.Slice(data[1], height*width/4) + data2 := unsafe.Slice(data[2], height*width/4) + for y := int32(0); y < height/2; y++ { + for x := int32(0); x < width/2; x++ { + data1[y*linesize[1]+x] = (uint8)(128 + y + frameIndex*2) + data2[y*linesize[2]+x] = (uint8)(64 + x + frameIndex*5) + } + } +} + +func main() { + var ret int32 + var dstBufsize int32 + var srcW int32 = 320 + var srcH int32 = 240 + var dstW, dstH int32 + var srcData, dstData [4]*uint8 + var srcLinesize, dstLinesize [4]int32 + var srcPixFmt = ffmpeg.AV_PIX_FMT_YUV420P + var dstPixFmt = ffmpeg.AV_PIX_FMT_RGB24 + + if len(os.Args) != 3 { + fmt.Fprintf(os.Stderr, "Usage: %s output_file output_size\n"+ + "API example program to show how to scale an image with libswscale.\n"+ + "This program generates a series of pictures, rescales them to the given "+ + "output_size and saves them to an output file named output_file\n."+ + "\n", os.Args[0]) + os.Exit(1) + } + dstFilename := os.Args[1] + dstSize := os.Args[2] + + if ret = ffmpeg.AvParseVideoSize(&dstW, &dstH, dstSize); ret < 0 { + fmt.Fprintf(os.Stderr, + "Invalid size '%s', must be in the form WxH or a valid size abbreviation\n", dstSize) + os.Exit(1) + } + + dstFile, err := os.OpenFile(dstFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not open %s\n", dstFilename) + os.Exit(1) + } + + // create scaling context + swsCtx := ffmpeg.SwsGetContext(srcW, srcH, srcPixFmt, + dstW, dstH, dstPixFmt, + ffmpeg.SWS_BILINEAR, nil, nil, nil) + if swsCtx == nil { + fmt.Fprintf(os.Stderr, "Impossible to create scale context for the conversion "+ + "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", + ffmpeg.AvGetPixFmtName(srcPixFmt), srcW, srcH, + ffmpeg.AvGetPixFmtName(dstPixFmt), dstW, dstH) + ret = ffmpeg.AVERROR(int32(syscall.EINVAL)) + goto end + } + + // allocate source and destination image buffers + if ret = ffmpeg.AvImageAlloc(srcData[:], srcLinesize[:], srcW, srcH, srcPixFmt, 16); ret < 0 { + fmt.Fprintf(os.Stderr, "Could not allocate source image\n") + goto end + } + + // buffer is going to be written to rawvideo file, no alignment + if ret = ffmpeg.AvImageAlloc(dstData[:], dstLinesize[:], dstW, dstH, dstPixFmt, 1); ret < 0 { + fmt.Fprintf(os.Stderr, "Could not allocate destination image\n") + goto end + } + dstBufsize = ret + + for i := int32(0); i < 100; i++ { + // generate synthetic video + fillYuvImage(srcData, srcLinesize, srcW, srcH, i) + + // convert to destination format + ffmpeg.SwsScale(swsCtx, srcData[:], srcLinesize[:], 0, srcH, dstData[:], dstLinesize[:]) + + // write scaled image to file + dstFile.Write(unsafe.Slice(dstData[0], dstBufsize)) + } + + fmt.Fprintf(os.Stderr, "Scaling succeeded. Play the output file with the command:\n"+ + "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", + ffmpeg.AvGetPixFmtName(dstPixFmt), dstW, dstH, dstFilename) + +end: + dstFile.Close() + ffmpeg.AvFreep(unsafe.Pointer(&srcData[0])) + ffmpeg.AvFreep(unsafe.Pointer(&dstData[0])) + ffmpeg.SwsFreeContext(swsCtx) + if ret < 0 { + os.Exit(1) + } } diff --git a/examples/transcode-aac/main.go b/examples/transcode-aac/main.go index 6080b57..e730e7c 100644 --- a/examples/transcode-aac/main.go +++ b/examples/transcode-aac/main.go @@ -447,7 +447,7 @@ func initOutputFrame(outputcodecContext *ffmpeg.AvCodecContext, // are assumed for simplicity. frame.SetNbSamples(frameSize) frame.SetChannelLayout(outputcodecContext.GetChannelLayout()) - frame.SetFormat(int32(outputcodecContext.GetSampleFmt())) + frame.SetFormat(outputcodecContext.GetSampleFmt()) frame.SetSampleRate(outputcodecContext.GetSampleRate()) // Allocate the samples of the created frame. This call will make diff --git a/ffmpeg.go b/ffmpeg.go index 4f6f9e9..31e276b 100644 --- a/ffmpeg.go +++ b/ffmpeg.go @@ -2,6 +2,6 @@ package ffmpeg /* #cgo CPPFLAGS: -Wno-deprecated-declarations -#cgo pkg-config: libavdevice libavformat libavfilter libavresample libavcodec libpostproc libswscale libswresample libavutil +#cgo pkg-config: libavdevice libavformat libavfilter libavcodec libpostproc libswscale libswresample libavutil */ import "C" diff --git a/postproc.go b/postproc.go index 26d0377..e807ffb 100644 --- a/postproc.go +++ b/postproc.go @@ -30,11 +30,17 @@ type PpContext C.pp_context type PpMode C.pp_mode // PpPostprocess -func PpPostprocess(src [3]*uint8, srcStride []int32, - dst [3]*uint8, dstStride []int32, +func PpPostprocess(src []*uint8, srcStride []int32, + dst []*uint8, dstStride []int32, horizontalSize, verticalSize int32, QPStore *int8, QPStride int32, ppMode *PpMode, ppContext *PpContext, pictType int32) { + if len(src) != 3 { + panic("src need len = 3") + } + if len(dst) != 3 { + panic("dst need len = 3") + } C.pp_postprocess((**C.uint8_t)(unsafe.Pointer(&src[0])), (*C.int)(&srcStride[0]), (**C.uint8_t)(unsafe.Pointer(&dst[0])), (*C.int)(&dstStride[0]), (C.int)(horizontalSize), (C.int)(verticalSize), diff --git a/swresample.go b/swresample.go index d51958d..cbb3ecb 100644 --- a/swresample.go +++ b/swresample.go @@ -11,7 +11,7 @@ const ( ) // Dithering algorithms -type SwrDitherType int32 +type SwrDitherType = C.enum_SwrDitherType const ( SWR_DITHER_NONE = SwrDitherType(C.SWR_DITHER_NONE) @@ -30,7 +30,7 @@ const ( ) // Resampling Engines -type SwrEngine int32 +type SwrEngine = C.enum_SwrEngine const ( SWR_ENGINE_SWR = SwrEngine(C.SWR_ENGINE_SWR) @@ -39,7 +39,7 @@ const ( ) // Resampling Filter Types -type SwrFilterType int32 +type SwrFilterType = C.enum_SwrFilterType const ( SWR_FILTER_TYPE_CUBIC = SwrFilterType(C.SWR_FILTER_TYPE_CUBIC) diff --git a/swscale.go b/swscale.go index e7b488c..fffe29d 100644 --- a/swscale.go +++ b/swscale.go @@ -109,12 +109,12 @@ func SwsInitContext(sctx *SwsContext, srcFilter, dstFilter *SwsFilter) int32 { } // SwsFreecontext frees the swscaler context swsContext. -func SwsFreecontext(sctx *SwsContext) { +func SwsFreeContext(sctx *SwsContext) { C.sws_freeContext((*C.struct_SwsContext)(sctx)) } // SwsGetcontext allocates and returns an SwsContext. -func SwsGetcontext(srcW, srcH int32, srcFormat AvPixelFormat, +func SwsGetContext(srcW, srcH int32, srcFormat AvPixelFormat, dstW, dstH int32, dstFormat AvPixelFormat, flags int32, srcFilter, dstFilter *SwsFilter, param *float64) *SwsContext { return (*SwsContext)(C.sws_getContext((C.int)(srcW), (C.int)(srcH), (C.enum_AVPixelFormat)(srcFormat), @@ -135,17 +135,29 @@ func SwsScale(sctx *SwsContext, srcSlice []*uint8, srcStride []int32, } // SwsSetColorspaceDetails -func SwsSetColorspaceDetails(sctx *SwsContext, invTable [4]int32, srcRange int32, - table [4]int32, dstRange int32, brightness, contrast, saturation int32) int32 { +func SwsSetColorSpaceDetails(sctx *SwsContext, invTable []int32, srcRange int32, + table []int32, dstRange int32, brightness, contrast, saturation int32) int32 { + if len(invTable) != 4 { + panic("invTable need len = 4") + } + if len(table) != 4 { + panic("table need len = 4") + } return (int32)(C.sws_setColorspaceDetails((*C.struct_SwsContext)(sctx), (*C.int)(unsafe.Pointer(&invTable[0])), (C.int)(srcRange), (*C.int)(unsafe.Pointer(&table[0])), (C.int)(dstRange), (C.int)(brightness), (C.int)(contrast), (C.int)(saturation))) } -// SwsGetColorspaceDetails -func SwsGetColorspaceDetails(sctx *SwsContext, invTable [4]int32, srcRange *int32, - table [4]int32, dstRange *int32, brightness, contrast, saturation *int32) int32 { +// SwsGetColorSpaceDetails +func SwsGetColorSpaceDetails(sctx *SwsContext, invTable []int32, srcRange *int32, + table []int32, dstRange *int32, brightness, contrast, saturation *int32) int32 { + if len(invTable) != 4 { + panic("invTable need len = 4") + } + if len(table) != 4 { + panic("table need len = 4") + } invTablePtr := unsafe.Pointer(&invTable[0]) tablePtr := unsafe.Pointer(&table[0]) return (int32)(C.sws_getColorspaceDetails((*C.struct_SwsContext)(sctx),