From e20be69f1780db135e6757694e2e1b5642f5330c Mon Sep 17 00:00:00 2001 From: aggresss Date: Tue, 31 Oct 2023 09:16:44 +0800 Subject: [PATCH] 2023-10-31 09:16:44 CST W44D2 --- avcodec.go | 21 +- avcodec_codec.go | 4 +- avcodec_mediacodec.go | 2 +- avcodec_packet.go | 14 +- avcodec_videotoolbox.go | 1 + avfilter.go | 6 +- avformat.go | 9 +- avutil.go | 3 +- avutil_adler32.go | 1 + avutil_audio_fifo.go | 1 + avutil_avstring.go | 1 + avutil_blowfish.go | 1 + avutil_bprint.go | 1 + avutil_buffer.go | 13 +- avutil_camellia.go | 1 + avutil_cast5.go | 1 + avutil_channel_layout.go | 5 +- avutil_common.go | 10 +- avutil_cpu.go | 2 +- avutil_dict.go | 1 + avutil_display.go | 6 + avutil_error.go | 2 +- avutil_file.go | 2 +- avutil_frame.go | 2 +- avutil_hwcontext.go | 8 + avutil_hwcontext_qsv.go | 15 ++ avutil_log.go | 10 +- avutil_macros.go | 1 + avutil_mathematics.go | 1 + avutil_motion_vector.go | 1 + avutil_murmur3.go | 1 + avutil_pixdesc.go | 1 + avutil_pixelutils.go | 1 + avutil_rational.go | 2 +- avutil_samplefmt.go | 2 +- avutil_spherical.go | 2 + avutil_stereo3d.go | 1 + avutil_threadmessage.go | 1 + avutil_timecode.go | 4 +- avutil_tree.go | 1 + avutil_twofish.go | 2 +- avutil_xtea.go | 1 + examples/demuxing-decoding/main.go | 2 +- examples/muxing/main.go | 2 +- examples/qsvdec/main.go | 189 +++++++++++++++++- examples/scaling-video/main.go | 2 +- examples/transcode-aac/main.go | 2 +- examples/transcoding/main.go | 2 +- examples/vaapi-transcode/main.go | 299 ++++++++++++++++++++++++++++- ffmpeg_helper.go | 52 ++--- swresample.go | 8 +- 51 files changed, 645 insertions(+), 78 deletions(-) diff --git a/avcodec.go b/avcodec.go index 027468c..1173fca 100644 --- a/avcodec.go +++ b/avcodec.go @@ -3472,6 +3472,11 @@ func (avctx *AVCodecContext) GetPropertiesAddr() *uint32 { return (*uint32)(&avctx.properties) } +const ( + FF_CODEC_PROPERTY_LOSSLESS = uint32(C.FF_CODEC_PROPERTY_LOSSLESS) + FF_CODEC_PROPERTY_CLOSED_CAPTIONS = uint32(C.FF_CODEC_PROPERTY_CLOSED_CAPTIONS) +) + // GetCodedSideData gets `AVCodecContext.coded_side_data` value. func (avctx *AVCodecContext) GetCodedSideData() *AVPacketSideData { return (*AVPacketSideData)(avctx.coded_side_data) @@ -3730,7 +3735,7 @@ func AvCodecGetLowres(avctx *AVCodecContext) int32 { // // AvCodecSetLowres func AvCodecSetLowres(avctx *AVCodecContext, i int32) { - C.av_codec_set_lowres((*C.struct_AVCodecContext)(avctx), C.int(i)) + C.av_codec_set_lowres((*C.struct_AVCodecContext)(avctx), (C.int)(i)) } // Deprecated: No use. @@ -3744,7 +3749,7 @@ func AvCodecGetSeekPreroll(avctx *AVCodecContext) int32 { // // AvCodecSetSeekPreroll func AvCodecSetSeekPreroll(avctx *AVCodecContext, i int32) { - C.av_codec_set_seek_preroll((*C.struct_AVCodecContext)(avctx), C.int(i)) + C.av_codec_set_seek_preroll((*C.struct_AVCodecContext)(avctx), (C.int)(i)) } // Deprecated: No use. @@ -3905,7 +3910,9 @@ const ( SUBTITLE_ASS = AVSubtitleType(C.SUBTITLE_ASS) ) -const AV_SUBTITLE_FLAG_FORCED = C.AV_SUBTITLE_FLAG_FORCED +const ( + AV_SUBTITLE_FLAG_FORCED = C.AV_SUBTITLE_FLAG_FORCED +) // AVSubtitleRect type AVSubtitleRect C.struct_AVSubtitleRect @@ -4290,13 +4297,13 @@ func AvSubtitleFree(s *AVSubtitle) { // The default callback for AVCodecContext.get_buffer2(). func AvCodecDefaultGetBuffer2(avctx *AVCodecContext, frame *AVFrame, flags int32) int32 { return (int32)(C.avcodec_default_get_buffer2((*C.struct_AVCodecContext)(avctx), - (*C.struct_AVFrame)(frame), C.int(flags))) + (*C.struct_AVFrame)(frame), (C.int)(flags))) } // The default callback for AVCodecContext.get_encode_buffer(). func AvCodecDefaultGetEncodeBuffer(avctx *AVCodecContext, pkt *AVPacket, flags int32) int32 { return (int32)(C.avcodec_default_get_encode_buffer((*C.struct_AVCodecContext)(avctx), - (*C.struct_AVPacket)(pkt), C.int(flags))) + (*C.struct_AVPacket)(pkt), (C.int)(flags))) } // AvCodecAlignDimensions modifies width and height values so that they will result in a memory @@ -4321,7 +4328,7 @@ func AvCodecEnumToChromaPos(xpos, ypos *int32, pos AVChromaLocation) int32 { // AvCodecChromaPosToEnum converts swscale x/y chroma position to AVChromaLocation. func AvCodecChromaPosToEnum(xpos, ypos int32) AVChromaLocation { - return (AVChromaLocation)(C.avcodec_chroma_pos_to_enum(C.int(xpos), C.int(ypos))) + return (AVChromaLocation)(C.avcodec_chroma_pos_to_enum((C.int)(xpos), (C.int)(ypos))) } // Deprecated: Use AVCodecSendPacket() and AVCodecReceiveFrame(). @@ -5258,7 +5265,7 @@ func AvCodecDefaultGetFormat(avctx *AVCodecContext, fmt *AVPixelFormat) AVPixelF (*C.enum_AVPixelFormat)(fmt))) } -// Deprecated: Use AvFourccMakeString() or AvFourcc2str() instead. +// Deprecated: Use AvFourccMakeString() or AvFourcc2str() instead. // // AvGetCodecTagString func AvGetCodecTagString(buf *int8, bufSize uintptr, codecTag uint32) int32 { diff --git a/avcodec_codec.go b/avcodec_codec.go index 8949dbb..9d9245b 100644 --- a/avcodec_codec.go +++ b/avcodec_codec.go @@ -155,12 +155,12 @@ func AvCodecFindEncoderByName(name string) *AVCodec { return (*AVCodec)(C.avcodec_find_encoder_by_name((*C.char)(namePtr))) } -// AvCodecIsEncoder returns a non-zero number if codec is an encoder, zero otherwise +// AvCodecIsEncoder returns a non-zero number if codec is an encoder, zero otherwise. func AvCodecIsEncoder(codec *AVCodec) int32 { return (int32)(C.av_codec_is_encoder((*C.struct_AVCodec)(codec))) } -// AvCodecIsDecoder returns a non-zero number if codec is an decoder, zero otherwise +// AvCodecIsDecoder returns a non-zero number if codec is an decoder, zero otherwise. func AvCodecIsDecoder(codec *AVCodec) int32 { return (int32)(C.av_codec_is_decoder((*C.struct_AVCodec)(codec))) } diff --git a/avcodec_mediacodec.go b/avcodec_mediacodec.go index fbafde5..6d2a955 100644 --- a/avcodec_mediacodec.go +++ b/avcodec_mediacodec.go @@ -25,7 +25,7 @@ func AvMediacodecDefaultInit(avctx *AVCodecContext, ctx *AVMediaCodecContext, su (*C.struct_AVMediaCodecContext)(ctx), VoidPointer(surface))) } -// AvMediacodecDefaultFree frees the MediaCodec context +// AvMediacodecDefaultFree frees the MediaCodec context. func AvMediacodecDefaultFree(avctx *AVCodecContext) { C.av_mediacodec_default_free((*C.struct_AVCodecContext)(avctx)) } diff --git a/avcodec_packet.go b/avcodec_packet.go index 1db44e2..7e6d37e 100644 --- a/avcodec_packet.go +++ b/avcodec_packet.go @@ -328,7 +328,7 @@ const ( AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS = AVSideDataParamChangeFlags(C.AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) ) -// AvPacketAlloc allocates an AVPacket and set its fields to default values. The resulting +// AvPacketAlloc allocates an AVPacket and set its fields to default values. The resulting // struct must be freed using AVPacketFree(). func AvPacketAlloc() *AVPacket { return (*AVPacket)(C.av_packet_alloc()) @@ -358,12 +358,12 @@ func AvNewPacket(pkt *AVPacket, size int32) int32 { return (int32)(C.av_new_packet((*C.struct_AVPacket)(pkt), (C.int)(size))) } -// AvShrinkPacket reduces packet size, correctly zeroing padding +// AvShrinkPacket reduces packet size, correctly zeroing padding. func AvShrinkPacket(pkt *AVPacket, size int32) { C.av_shrink_packet((*C.struct_AVPacket)(pkt), (C.int)(size)) } -// AvGrowPacket increases packet size, correctly zeroing padding +// AvGrowPacket increases packet size, correctly zeroing padding. func AvGrowPacket(pkt *AVPacket, growBy int32) int32 { return (int32)(C.av_grow_packet((*C.struct_AVPacket)(pkt), (C.int)(growBy))) } @@ -383,14 +383,14 @@ func AvDupPacket(pkt *AVPacket) { // Deprecated: Use AVPacketRef instead. // -// AvCopyPacket copies packet, including contents +// AvCopyPacket copies packet, including contents. func AvCopyPacket(dst, src *AVPacket) int32 { return (int32)(C.av_copy_packet((*C.struct_AVPacket)(dst), (*C.struct_AVPacket)(src))) } // Deprecated: Use AVPacketCopyProps instead. // -// AvCopyPacketSideData copies packet side data +// AvCopyPacketSideData copies packet side data. func AvCopyPacketSideData(dst, src *AVPacket) int32 { return (int32)(C.av_copy_packet_side_data((*C.struct_AVPacket)(dst), (*C.struct_AVPacket)(src))) } @@ -457,7 +457,7 @@ func AvPacketFreeSideData(pkt *AVPacket) { C.av_packet_free_side_data((*C.struct_AVPacket)(pkt)) } -// AvPacketRef setups a new reference to the data described by a given packet +// AvPacketRef setups a new reference to the data described by a given packet. func AvPacketRef(dst, src *AVPacket) int32 { return (int32)(C.av_packet_ref((*C.struct_AVPacket)(dst), (*C.struct_AVPacket)(src))) } @@ -483,7 +483,7 @@ func AvPacketMakeRefcounted(pkt *AVPacket) { C.av_packet_make_refcounted((*C.struct_AVPacket)(pkt)) } -// AvPacketMakeWritable creates a writable reference for the data described by a given packet, +// AvPacketMakeWritable creates a writable reference for the data described by a given packet, // avoiding data copy if possible. func AvPacketMakeWritable(pkt *AVPacket) { C.av_packet_make_writable((*C.struct_AVPacket)(pkt)) diff --git a/avcodec_videotoolbox.go b/avcodec_videotoolbox.go index 302638c..e219cf9 100644 --- a/avcodec_videotoolbox.go +++ b/avcodec_videotoolbox.go @@ -11,6 +11,7 @@ package ffmpeg */ import "C" +// AVVideotoolboxContext type AVVideotoolboxContext C.struct_AVVideotoolboxContext // AvVideotoolboxAllocContext allocates and initializes a Videotoolbox context. diff --git a/avfilter.go b/avfilter.go index f77b6da..b1e79b6 100644 --- a/avfilter.go +++ b/avfilter.go @@ -180,7 +180,7 @@ func (fltc *AVFilterContext) GetName() string { // SetName sets `AVFilterContext.name` value. func (fltc *AVFilterContext) SetName(v string) { - C.free(unsafe.Pointer(fltc.name)) + FreePointer(fltc.name) fltc.name, _ = StringCasting(v) } @@ -735,7 +735,7 @@ const ( AVFILTER_CMD_FLAG_FAST = C.AVFILTER_CMD_FLAG_FAST ) -// AvFilterProcessCommand makes the filter instance process a command. +// AvFilterProcessCommand makes the filter instance process a command. // It is recommended to use AVFilterGraphSendCommand(). func AvFilterProcessCommand(filter *AVFilterContext, cmd, arg string, resLen, flags int32) (res string, ret int32) { cmdPtr, cmdFunc := StringCasting(cmd) @@ -1032,7 +1032,7 @@ func (fltio *AVFilterInOut) GetName() string { // SetName sets `AVFilterInOut.name` value. func (fltio *AVFilterInOut) SetName(v string) { - C.free(unsafe.Pointer(fltio.name)) + FreePointer(fltio.name) fltio.name, _ = StringCasting(v) } diff --git a/avformat.go b/avformat.go index 3068c02..f252f9a 100644 --- a/avformat.go +++ b/avformat.go @@ -982,8 +982,13 @@ func (cpt *AVChapter) GetMetadataAddr() **AVDictionary { return (**AVDictionary)(unsafe.Pointer(&cpt.metadata)) } +// typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, +// void *data, size_t data_size); type AVFormatControlMessageFunc C.av_format_control_message +// typedef int (*AVOpenCallback)(struct AVFormatContext *s, +// AVIOContext **pb, const char *url, int flags, +// const AVIOInterruptCB *int_cb, AVDictionary **options); type AVOpenCallbackFunc C.AVOpenCallback // AVDurationEstimationMethod @@ -1140,7 +1145,7 @@ func (s *AVFormatContext) GetUrl() string { func (s *AVFormatContext) SetUrl(v string) { vPtr, _ := StringCasting(v) if s.url != nil { - C.free(unsafe.Pointer(s.url)) + FreePointer(s.url) } s.url = (*C.char)(vPtr) } @@ -2396,7 +2401,7 @@ func AvProbeInputBuffer2(pb *AVIOContext, fmt **AVInputFormat, (*C.char)(urlPtr), VoidPointer(logctx), (C.uint)(offset), (C.uint)(maxProbeSize))) } -// AvProbeInputBuffer likes AvProbeInputBuffer2() but returns 0 on success +// AvProbeInputBuffer likes AvProbeInputBuffer2() but returns 0 on success. func AvProbeInputBuffer(pb *AVIOContext, fmt **AVInputFormat, url string, logctx CVoidPointer, offset, maxProbeSize uint32) int32 { urlPtr, urlFunc := StringCasting(url) diff --git a/avutil.go b/avutil.go index bf0bc6c..2d4407f 100644 --- a/avutil.go +++ b/avutil.go @@ -34,7 +34,7 @@ func AvutilLicense() string { return C.GoString(C.avutil_license()) } -// Media Type +// Media Type. type AVMediaType = C.enum_AVMediaType const ( @@ -99,6 +99,7 @@ func AvXIfNull[T any](p, x *T) *T { return x } +// AvStringIfNull func AvStringIfNull(p, x string) string { if len(p) != 0 { return p diff --git a/avutil_adler32.go b/avutil_adler32.go index b25d810..9039165 100644 --- a/avutil_adler32.go +++ b/avutil_adler32.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// AVAdler type AVAdler = C.AVAdler // AvAdler32Update calculates the Adler32 checksum of a buffer. diff --git a/avutil_audio_fifo.go b/avutil_audio_fifo.go index 275df69..5390f09 100644 --- a/avutil_audio_fifo.go +++ b/avutil_audio_fifo.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// AVAudioFifo type AVAudioFifo C.struct_AVAudioFifo // AvAudioFifoFree frees an AVAudioFifo. diff --git a/avutil_avstring.go b/avutil_avstring.go index f4872e1..3bc6a9f 100644 --- a/avutil_avstring.go +++ b/avutil_avstring.go @@ -57,6 +57,7 @@ import "C" // NONEED: av_append_path_component +// AVEscapeMode type AVEscapeMode = C.enum_AVEscapeMode const ( diff --git a/avutil_blowfish.go b/avutil_blowfish.go index c041763..32eb8ae 100644 --- a/avutil_blowfish.go +++ b/avutil_blowfish.go @@ -14,6 +14,7 @@ const ( AV_BF_ROUNDS = C.AV_BF_ROUNDS ) +// AVBlowfish type AVBlowfish C.struct_AVBlowfish // GetP gets `AVBlowfish.p` value. diff --git a/avutil_bprint.go b/avutil_bprint.go index 0f0c41a..d9fb967 100644 --- a/avutil_bprint.go +++ b/avutil_bprint.go @@ -17,6 +17,7 @@ import ( "unsafe" ) +// AVBPrint type AVBPrint C.struct_AVBPrint const ( diff --git a/avutil_buffer.go b/avutil_buffer.go index 74474a9..6d3e335 100644 --- a/avutil_buffer.go +++ b/avutil_buffer.go @@ -47,9 +47,11 @@ func AvBufferAllocz(size int32) *AVBufferRef { return (*AVBufferRef)(C.av_buffer_allocz((C.int)(size))) } -const AV_BUFFER_FLAG_READONLY = C.AV_BUFFER_FLAG_READONLY +const ( + AV_BUFFER_FLAG_READONLY = C.AV_BUFFER_FLAG_READONLY +) -// typedef void (*av_buffer_free_func)(void *opaque, uint8_t *data) +// typedef void (*av_buffer_free_func)(void *opaque, uint8_t *data); type AVBufferFreeFunc = C.av_buffer_free_func // AvBufferCreate Create an AVBuffer from an existing array. @@ -108,15 +110,16 @@ func AvBufferReplace(dst **AVBufferRef, src *AVBufferRef) int32 { (*C.struct_AVBufferRef)(src))) } +// AVBufferPool type AVBufferPool C.struct_AVBufferPool -// typedef AVBufferRef* (*av_buffer_pool_alloc_func)(int size) +// typedef AVBufferRef* (*av_buffer_pool_alloc_func)(int size); type AVBufferPoolAllocFunc = C.av_buffer_pool_alloc_func -// typedef AVBufferRef* (*av_buffer_pool_alloc2_func)(void* opaque, int size) +// typedef AVBufferRef* (*av_buffer_pool_alloc2_func)(void* opaque, int size); type AVBufferPoolAlloc2Func = C.av_buffer_pool_alloc2_func -// typedef void (*av_buffer_pool_free_func)(void* opaque) +// typedef void (*av_buffer_pool_free_func)(void* opaque); type AVBufferPoolFreeFunc = C.av_buffer_pool_free_func // AvBufferPoolInit allocates and initializes a buffer pool. diff --git a/avutil_camellia.go b/avutil_camellia.go index 84d69e0..f9dd21f 100644 --- a/avutil_camellia.go +++ b/avutil_camellia.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// AVCAMELLIA type AVCAMELLIA C.struct_AVCAMELLIA // AvCamelliaAlloc allocates an AVCAMELLIA context. diff --git a/avutil_cast5.go b/avutil_cast5.go index 72768a9..65fdfc2 100644 --- a/avutil_cast5.go +++ b/avutil_cast5.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// AVCAST5 type AVCAST5 C.struct_AVCAST5 // AvCast5Alloc allocates an AVCAST5 context. diff --git a/avutil_channel_layout.go b/avutil_channel_layout.go index 54e39f3..f817a7a 100644 --- a/avutil_channel_layout.go +++ b/avutil_channel_layout.go @@ -77,6 +77,7 @@ const ( AV_CH_LAYOUT_22POINT2 = uint64(C.AV_CH_LAYOUT_22POINT2) ) +// AVMatrixEncoding type AVMatrixEncoding = C.enum_AVMatrixEncoding const ( @@ -105,7 +106,9 @@ func AvGetExtendedChannelLayout(name string, channelLayout *uint64, nbChannels * (*C.uint64_t)(channelLayout), (*C.int32_t)(nbChannels))) } -const AV_CH_LAYOUT_MAX_STRING_SIZE = 256 +const ( + AV_CH_LAYOUT_MAX_STRING_SIZE = 256 +) // AvGetChannelLayoutString returns a description of a channel layout. func AvGetChannelLayoutString(nbChannels int32, channelLayout uint64) string { diff --git a/avutil_common.go b/avutil_common.go index 2b5f758..2b8e7a4 100644 --- a/avutil_common.go +++ b/avutil_common.go @@ -56,6 +56,7 @@ func FFUMOD[T Integer](a, b T) T { return a - b*FFUDIV(a, b) } +// FFABS func FFABS[T SingedInteger](a T) T { if a >= 0 { return a @@ -63,6 +64,7 @@ func FFABS[T SingedInteger](a T) T { return -a } +// FFSIGNT func FFSIGNT[T SingedInteger](a T) T { if a > 0 { return 1 @@ -105,6 +107,7 @@ func FFDIFFSIGN[T Integer](x, y T) int { } } +// FFMAX func FFMAX[T Integer](a, b T) T { if a > b { return a @@ -112,10 +115,12 @@ func FFMAX[T Integer](a, b T) T { return b } +// FFMAX3 func FFMAX3[T Integer](a, b, c T) T { return FFMAX(FFMAX(a, b), c) } +// FFMIN func FFMIN[T Integer](a, b T) T { if a > b { return b @@ -123,6 +128,7 @@ func FFMIN[T Integer](a, b T) T { return a } +// FFMIN3 func FFMIN3[T Integer](a, b, c T) T { return FFMIN(FFMIN(a, b), c) } @@ -241,12 +247,12 @@ func AvCeilLog2C(x int32) int32 { return (int32)(C.av_ceil_log2_c((C.int)(x))) } -// AvPopcountC counts number of bits set to one in x +// AvPopcountC counts number of bits set to one in x. func AvPopcountC(x uint32) int32 { return (int32)(C.av_popcount_c((C.uint)(x))) } -// AvPopcount64C counts number of bits set to one in x +// AvPopcount64C counts number of bits set to one in x. func AvPopcount64C(x uint64) int32 { return (int32)(C.av_popcount64_c((C.uint64_t)(x))) } diff --git a/avutil_cpu.go b/avutil_cpu.go index 010a683..6c62f9b 100644 --- a/avutil_cpu.go +++ b/avutil_cpu.go @@ -68,7 +68,7 @@ func AvForceCpuFlags(flags int32) { C.av_force_cpu_flags((C.int)(flags)) } -// Deprecated: Use AvForceCpuFlags() and AvGetCpuFlags() instead +// Deprecated: Use AvForceCpuFlags() and AvGetCpuFlags() instead. // // AvSetCpuFlagsMask set a mask on flags returned by AvGetCpuFlags(). func AvSetCpuFlagsMask(mask int32) { diff --git a/avutil_dict.go b/avutil_dict.go index 7cc1799..8f83505 100644 --- a/avutil_dict.go +++ b/avutil_dict.go @@ -20,6 +20,7 @@ const ( AV_DICT_MULTIKEY = C.AV_DICT_MULTIKEY ) +// AVDictionaryEntry type AVDictionaryEntry C.struct_AVDictionaryEntry // GetKey gets `AVDictionaryEntry.key` value. diff --git a/avutil_display.go b/avutil_display.go index 747ef8b..6aa24bd 100644 --- a/avutil_display.go +++ b/avutil_display.go @@ -20,10 +20,16 @@ func AvDisplayRotationGet(matrix []int32) float64 { // AvDisplayRotationSet initializes a transformation matrix describing a pure counterclockwise // rotation by the specified angle (in degrees). func AvDisplayRotationSet(matrix []int32, angle float64) { + if len(matrix) < 9 { + panic("matrix len < 9") + } C.av_display_rotation_set((*C.int32_t)(&matrix[0]), (C.double)(angle)) } // AvDisplayMatrixFlip flips the input matrix horizontally and/or vertically. func AvDisplayMatrixFlip(matrix []int32, hflip, vflip int32) { + if len(matrix) < 9 { + panic("matrix len < 9") + } C.av_display_matrix_flip((*C.int32_t)(&matrix[0]), (C.int)(hflip), (C.int)(vflip)) } diff --git a/avutil_error.go b/avutil_error.go index 6705943..42d48d2 100644 --- a/avutil_error.go +++ b/avutil_error.go @@ -27,7 +27,7 @@ func AVUNERROR(e int32) int32 { return e } -// Error handling +// Error handling. const ( AVERROR_BSF_NOT_FOUND = int32(C.AVERROR_BSF_NOT_FOUND) AVERROR_BUG = int32(C.AVERROR_BUG) diff --git a/avutil_file.go b/avutil_file.go index 4cc0a9e..697e3e1 100644 --- a/avutil_file.go +++ b/avutil_file.go @@ -29,7 +29,7 @@ func AvTempfile(prefix string, logOffset int32, logCtx CVoidPointer) (filename s prefixPtr, prefixFunc := StringCasting(prefix) defer prefixFunc() var filenamePtr *C.char - defer C.free(unsafe.Pointer(filenamePtr)) + defer FreePointer(filenamePtr) ret = (int32)(C.av_tempfile((*C.char)(prefixPtr), (**C.char)(unsafe.Pointer(&filenamePtr)), (C.int)(logOffset), diff --git a/avutil_frame.go b/avutil_frame.go index 9d0d0a9..9e93ab7 100644 --- a/avutil_frame.go +++ b/avutil_frame.go @@ -1363,7 +1363,7 @@ func AvFrameApplyCropping(frame *AVFrame, flags int32) int32 { return (int32)(C.av_frame_apply_cropping((*C.struct_AVFrame)(frame), (C.int)(flags))) } -// AvFrameSideDataName returns a string identifying the side data type +// AvFrameSideDataName returns a string identifying the side data type. func AvFrameSideDataName(_type AVFrameSideDataType) string { return C.GoString(C.av_frame_side_data_name((C.enum_AVFrameSideDataType)(_type))) } diff --git a/avutil_hwcontext.go b/avutil_hwcontext.go index fece950..372593e 100644 --- a/avutil_hwcontext.go +++ b/avutil_hwcontext.go @@ -31,10 +31,13 @@ const ( AV_HWDEVICE_TYPE_VULKAN = AVHWDeviceType(C.AV_HWDEVICE_TYPE_VULKAN) ) +// AVHWDeviceInternal type AVHWDeviceInternal C.struct_AVHWDeviceInternal +// AVHWDeviceContext type AVHWDeviceContext C.struct_AVHWDeviceContext +// typedef void (*av_hw_device_context_free_func)(struct AVHWDeviceContext *ctx); type AVHWDeviceContextFreeFunc = C.av_hw_device_context_free_func // GetAvClass gets `AVHWDeviceContext.av_class` value. @@ -117,10 +120,13 @@ func (dc *AVHWDeviceContext) GetUserOpaqueAddr() *unsafe.Pointer { return &dc.user_opaque } +// AVHWFramesInternal type AVHWFramesInternal C.struct_AVHWFramesInternal +// AVHWFramesContext type AVHWFramesContext C.struct_AVHWFramesContext +// typedef void (*av_hw_frames_context_free_func)(struct AVHWFramesContext *ctx); type AVHWFramesContextFreeFunc = C.av_hw_frames_context_free_func // GetAvClass gets `AVHWFramesContext.av_class` value. @@ -393,6 +399,7 @@ func AvHWFrameTransferData(dst, src *AVFrame, flags int32) int32 { (C.int)(flags))) } +// AVHWFrameTransferDirection type AVHWFrameTransferDirection = C.enum_AVHWFrameTransferDirection const ( @@ -410,6 +417,7 @@ func AvHWFrameTransferGetFormats(hwframeCtx *AVBufferRef, dir AVHWFrameTransferD (C.int)(flags))) } +// AVHWFramesConstraints type AVHWFramesConstraints C.struct_AVHWFramesConstraints // GetValidHwFormats gets `AVHWFramesConstraints.valid_hw_formats` value. diff --git a/avutil_hwcontext_qsv.go b/avutil_hwcontext_qsv.go index b2a7a5f..d36df32 100644 --- a/avutil_hwcontext_qsv.go +++ b/avutil_hwcontext_qsv.go @@ -16,3 +16,18 @@ type AVQSVDeviceContext C.struct_AVQSVDeviceContext // AVQSVFramesContext type AVQSVFramesContext C.struct_AVQSVFramesContext + +// GetFrameType gets `AVQSVFramesContext.frame_type` value. +func (ctx *AVQSVFramesContext) GetFrameType() int32 { + return (int32)(ctx.frame_type) +} + +// SetFrameType sets `AVQSVFramesContext.frame_type` value. +func (ctx *AVQSVFramesContext) SetFrameType(v int32) { + ctx.frame_type = (C.int)(v) +} + +// GetFrameTypeAddr gets `AVQSVFramesContext.frame_type` address. +func (ctx *AVQSVFramesContext) GetFrameTypeAddr() *int32 { + return (*int32)(&ctx.frame_type) +} diff --git a/avutil_log.go b/avutil_log.go index 4656243..f39c9c0 100644 --- a/avutil_log.go +++ b/avutil_log.go @@ -199,27 +199,27 @@ func AvLogOnce(avcl CVoidPointer, initialLevel, subsequentLevel int32, state *in // NONEED: av_vlog -// AvLogGetLevel gets the current log level +// AvLogGetLevel gets the current log level. func AvLogGetLevel() int32 { return (int32)(C.av_log_get_level()) } -// AvLogSetLevel sets the log level +// AvLogSetLevel sets the log level. func AvLogSetLevel(level int32) { - C.av_log_set_level(C.int(level)) + C.av_log_set_level((C.int)(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 +// 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 +// AvDefaultItemName returns the context name. func AvDefaultItemName(ctx CVoidPointer) string { return C.GoString(C.av_default_item_name(VoidPointer(ctx))) } diff --git a/avutil_macros.go b/avutil_macros.go index a96f83d..590993e 100644 --- a/avutil_macros.go +++ b/avutil_macros.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// FFALIGN func FFALIGN[T Integer](x, a T) T { return ((x) + (a) - 1) & ^((a) - 1) } diff --git a/avutil_mathematics.go b/avutil_mathematics.go index 91acb07..04dfa3f 100644 --- a/avutil_mathematics.go +++ b/avutil_mathematics.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// AVRounding type AVRounding = C.enum_AVRounding const ( diff --git a/avutil_motion_vector.go b/avutil_motion_vector.go index 5afb4ee..d7b224a 100644 --- a/avutil_motion_vector.go +++ b/avutil_motion_vector.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// AVMotionVector type AVMotionVector C.struct_AVMotionVector // GetSource gets `AVMotionVector.source` value. diff --git a/avutil_murmur3.go b/avutil_murmur3.go index e923b00..1b15e3a 100644 --- a/avutil_murmur3.go +++ b/avutil_murmur3.go @@ -9,6 +9,7 @@ package ffmpeg */ import "C" +// AVMurMur3 type AVMurMur3 C.struct_AVMurMur3 // AvMurmur3Alloc allocates an AVMurMur3 hash context. diff --git a/avutil_pixdesc.go b/avutil_pixdesc.go index 18ba3ed..fa851a7 100644 --- a/avutil_pixdesc.go +++ b/avutil_pixdesc.go @@ -10,6 +10,7 @@ package ffmpeg import "C" import "unsafe" +// AVComponentDescriptor type AVComponentDescriptor C.struct_AVComponentDescriptor // GetPlane gets `AVComponentDescriptor.plane` value. diff --git a/avutil_pixelutils.go b/avutil_pixelutils.go index 44bb8b7..709c55d 100644 --- a/avutil_pixelutils.go +++ b/avutil_pixelutils.go @@ -10,6 +10,7 @@ package ffmpeg */ import "C" +// AVPixelutilsSadFn type AVPixelutilsSadFn C.av_pixelutils_sad_fn // AvPixelutilsGetSadFn gets a potentially optimized pointer to a Sum-of-absolute-differences diff --git a/avutil_rational.go b/avutil_rational.go index df361ac..a06a252 100644 --- a/avutil_rational.go +++ b/avutil_rational.go @@ -87,7 +87,7 @@ func AvD2Q(d float64, max int32) AVRational { // return One of the following values: // 1 if `q1` is nearer to `q` than `q2` // -1 if `q2` is nearer to `q` than `q1` -// 0 if they have the same distance +// 0 if they have the same distance. func AvNearerQ(q, q1, q2 AVRational) int32 { return (int32)(C.av_nearer_q((C.struct_AVRational)(q), (C.struct_AVRational)(q1), (C.struct_AVRational)(q2))) diff --git a/avutil_samplefmt.go b/avutil_samplefmt.go index 3ac53ad..60588be 100644 --- a/avutil_samplefmt.go +++ b/avutil_samplefmt.go @@ -49,7 +49,7 @@ func AvGetSampleFmt(name string) AVSampleFormat { // requested planar/packed format, the format returned is the same as the // input. func AvGetAltSampleFmt(sampleFmt AVSampleFormat, planar int32) AVSampleFormat { - return (AVSampleFormat)(C.av_get_alt_sample_fmt((C.enum_AVSampleFormat)(sampleFmt), C.int(planar))) + return (AVSampleFormat)(C.av_get_alt_sample_fmt((C.enum_AVSampleFormat)(sampleFmt), (C.int)(planar))) } // AvGetPackedSampleFmt gets the packed alternative form of the given sample format. diff --git a/avutil_spherical.go b/avutil_spherical.go index 36b8601..be76dbd 100644 --- a/avutil_spherical.go +++ b/avutil_spherical.go @@ -10,6 +10,7 @@ package ffmpeg import "C" import "unsafe" +// AVSphericalProjection type AVSphericalProjection = C.enum_AVSphericalProjection const ( @@ -18,6 +19,7 @@ const ( AV_SPHERICAL_EQUIRECTANGULAR_TILE = AVSphericalProjection(C.AV_SPHERICAL_EQUIRECTANGULAR_TILE) ) +// AVSphericalMapping type AVSphericalMapping C.struct_AVSphericalMapping // GetProjection gets `AVSphericalMapping.projection` value. diff --git a/avutil_stereo3d.go b/avutil_stereo3d.go index 762e364..e00bf63 100644 --- a/avutil_stereo3d.go +++ b/avutil_stereo3d.go @@ -36,6 +36,7 @@ const ( AV_STEREO3D_FLAG_INVERT = C.AV_STEREO3D_FLAG_INVERT ) +// AVStereo3D type AVStereo3D C.struct_AVStereo3D // GetType gets `AVStereo3D.type` value. diff --git a/avutil_threadmessage.go b/avutil_threadmessage.go index 5b75b46..e411d2f 100644 --- a/avutil_threadmessage.go +++ b/avutil_threadmessage.go @@ -12,6 +12,7 @@ typedef void (*av_thread_message_free_func)(void *msg); import "C" import "unsafe" +// AVThreadMessageQueue type AVThreadMessageQueue C.struct_AVThreadMessageQueue // AVThreadMessageFlags diff --git a/avutil_timecode.go b/avutil_timecode.go index f66e764..a761fc4 100644 --- a/avutil_timecode.go +++ b/avutil_timecode.go @@ -13,6 +13,7 @@ const ( AV_TIMECODE_STR_SIZE = C.AV_TIMECODE_STR_SIZE ) +// AVTimecodeFlag type AVTimecodeFlag = C.enum_AVTimecodeFlag const ( @@ -21,6 +22,7 @@ const ( AV_TIMECODE_FLAG_ALLOWNEGATIVE = AVTimecodeFlag(C.AV_TIMECODE_FLAG_ALLOWNEGATIVE) ) +// AVTimecode type AVTimecode C.AVTimecode // GetStart gets `AVTimecode.start` value. @@ -151,7 +153,7 @@ func AvTimecodeInitFromString(tc *AVTimecode, rate AVRational, str string, logCt (*C.char)(strPtr), VoidPointer(logCtx))) } -// AvTimecodeCheckFrameRate checks if the timecode feature is available for the given frame rate +// AvTimecodeCheckFrameRate checks if the timecode feature is available for the given frame rate. func AvTimecodeCheckFrameRate(rate AVRational) int32 { return (int32)(C.av_timecode_check_frame_rate((C.struct_AVRational)(rate))) } diff --git a/avutil_tree.go b/avutil_tree.go index dacb5a9..e48a2b3 100644 --- a/avutil_tree.go +++ b/avutil_tree.go @@ -14,6 +14,7 @@ typedef int (*av_tree_enu_func)(void *opaque, void *elem); import "C" import "unsafe" +// AVTreeNode type AVTreeNode C.struct_AVTreeNode // typedef int (*av_tree_cmp_func)(void *opaque, void *elem); diff --git a/avutil_twofish.go b/avutil_twofish.go index 04b2be1..d98f687 100644 --- a/avutil_twofish.go +++ b/avutil_twofish.go @@ -23,7 +23,7 @@ func AvTwofishInit(d *AVTWOFISH, key *uint8, keyBits int32) int32 { (*C.uint8_t)(key), (C.int)(keyBits))) } -// AvTwofishCrypt encrypts or decrypts a buffer using a previously initialized context +// AvTwofishCrypt encrypts or decrypts a buffer using a previously initialized context. func AvTwofishCrypt(d *AVTWOFISH, dst, src *uint8, count int32, iv *uint8, decrypt int32) { C.av_twofish_crypt((*C.struct_AVTWOFISH)(d), (*C.uint8_t)(dst), (*C.uint8_t)(src), diff --git a/avutil_xtea.go b/avutil_xtea.go index dc51dd2..2d6916f 100644 --- a/avutil_xtea.go +++ b/avutil_xtea.go @@ -10,6 +10,7 @@ package ffmpeg import "C" import "unsafe" +// AVXTEA type AVXTEA C.struct_AVXTEA // GetKey gets `AVXTEA.key` value. diff --git a/examples/demuxing-decoding/main.go b/examples/demuxing-decoding/main.go index 296ed92..afdac64 100644 --- a/examples/demuxing-decoding/main.go +++ b/examples/demuxing-decoding/main.go @@ -302,7 +302,7 @@ func main() { if videoStream != nil { fmt.Fprintf(os.Stdout, "Play the output video file with the command:\n"+ - "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", + "ffplay -f rawvideo -pixel_format %s -video_size %dx%d %s\n", ffmpeg.AvGetPixFmtName(videoDecCtx.GetPixFmt()), videoDecCtx.GetWidth(), videoDecCtx.GetHeight(), videoDstFilename) } diff --git a/examples/muxing/main.go b/examples/muxing/main.go index de70e85..ebdbb6d 100644 --- a/examples/muxing/main.go +++ b/examples/muxing/main.go @@ -401,7 +401,7 @@ func getVideoFrame(ost *outputStream) *ffmpeg.AVFrame { return nil } - // when we pass a frame to the encoder, it may keep a reference to it + // when we pass a frame to the encoder, it may keep a reference to it // internally; make sure we do not overwrite it here if ffmpeg.AvFrameMakeWritable(ost.frame) < 0 { panic("Make video frame writable failed") diff --git a/examples/qsvdec/main.go b/examples/qsvdec/main.go index 7905807..47e99c0 100644 --- a/examples/qsvdec/main.go +++ b/examples/qsvdec/main.go @@ -1,5 +1,192 @@ +//go:build ffmpeg_hw_qsv + package main -func main() { +/* +#include +struct AVCodecContext; + +int get_format(struct AVCodecContext *avctx, int *pix_fmts) { + // TODO. + return -1; +} + +*/ +import "C" + +import ( + "fmt" + "os" + "syscall" + "unsafe" + + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +type decodeContext struct { + hwDeviceRef *ffmpeg.AVBufferRef +} + +func decodePacket(decode *decodeContext, decoderCtx *ffmpeg.AVCodecContext, + frame *ffmpeg.AVFrame, swFrame *ffmpeg.AVFrame, + pkt *ffmpeg.AVPacket, outputCtx *ffmpeg.AVIOContext) (ret int32) { + var ( + data [][]uint8 + ) + if ret = ffmpeg.AvCodecSendPacket(decoderCtx, pkt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during decoding\n") + return ret + } + + for ret >= 0 { + ret = ffmpeg.AvCodecReceiveFrame(decoderCtx, frame) + if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF { + break + } else if ret < 0 { + fmt.Fprintf(os.Stderr, "Error while decoding.\n") + return ret + } + + // A real program would do something useful with the decoded frame here. + // We just retrieve the raw data and write it to a file, which is rather + // useless but pedagogic. + if ret = ffmpeg.AvHWFrameTransferData(swFrame, frame, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Error transferring the data to system memory\n") + goto fail + } + + data = ffmpeg.SliceSlice(&swFrame.GetData()[0], len(swFrame.GetData()), + swFrame.GetWidth()*swFrame.GetHeight()) + for i := 0; i < len(swFrame.GetData()) && data[i] != nil; i++ { + for j := 0; j < int(swFrame.GetHeight()>>ffmpeg.CondExpr(i > 0, 1, 0)); j++ { + ffmpeg.AvIOWrite(outputCtx, &data[i][j*int(swFrame.GetLinesize()[i])], swFrame.GetWidth()) + } + } + fail: + ffmpeg.AvFrameUnref(swFrame) + ffmpeg.AvFrameUnref(frame) + + if ret < 0 { + return ret + } + } + + return 0 +} + +func main() { + var ( + intputCtx *ffmpeg.AVFormatContext + videoSt *ffmpeg.AVStream + decoderCtx *ffmpeg.AVCodecContext + decoder *ffmpeg.AVCodec + pkt ffmpeg.AVPacket + frame, swFrame *ffmpeg.AVFrame + decode = &decodeContext{} + outputCtx *ffmpeg.AVIOContext + ret int32 + ) + + if len(os.Args) < 3 { + fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0]) + os.Exit(1) + } + + // open the input file + if ret = ffmpeg.AvFormatOpenInput(&intputCtx, os.Args[1], nil, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open input file '%s': ", os.Args[1]) + goto finish + } + + // find the first H.264 video stream + for i := 0; i < int(intputCtx.GetNbStreams()); i++ { + st := intputCtx.GetStreams()[i] + if st.GetCodecpar().GetCodecId() == ffmpeg.AV_CODEC_ID_H264 && videoSt == nil { + videoSt = st + } else { + st.SetDiscard(ffmpeg.AVDISCARD_ALL) + } + } + if videoSt == nil { + fmt.Fprintf(os.Stderr, "No H.264 video stream in the input file\n") + goto finish + } + + // open the hardware device + if ret = ffmpeg.AvHWDeviceCtxCreate(&decode.hwDeviceRef, ffmpeg.AV_HWDEVICE_TYPE_QSV, + "auto", nil, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open the hardware device\n") + goto finish + } + + // initialize the decoder + if decoder = ffmpeg.AvCodecFindDecoderByName("h264_qsv"); decoder == nil { + fmt.Fprintf(os.Stderr, "The QSV decoder is not present in libavcodec\n") + goto finish + } + + decoderCtx.SetCodecId(ffmpeg.AV_CODEC_ID_H264) + if videoSt.GetCodecpar().GetExtradataSize() != 0 { + decoderCtx.SetExtradata((*uint8)(ffmpeg.AvMallocz(videoSt.GetCodecpar().GetExtradataSize() + + ffmpeg.AV_INPUT_BUFFER_PADDING_SIZE))) + if decoderCtx.GetExtradata() == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto finish + } + copy(unsafe.Slice(decoderCtx.GetExtradata(), videoSt.GetCodecpar().GetExtradataSize()), + unsafe.Slice(videoSt.GetCodecpar().GetExtradata(), videoSt.GetCodecpar().GetExtradataSize())) + } + + decoderCtx.SetOpaque(decode.hwDeviceRef) + decoderCtx.SetGetFormat((ffmpeg.AVCodecContextGetFormatFunc)(C.get_format)) + + if ret = ffmpeg.AvCodecOpen2(decoderCtx, nil, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Error opening the decoder: ") + goto finish + } + + // open the output stream + if ret = ffmpeg.AvIOOpen(&outputCtx, os.Args[2], ffmpeg.AVIO_FLAG_WRITE); ret < 0 { + fmt.Fprintf(os.Stderr, "Error opening the output context: ") + goto finish + } + + frame = ffmpeg.AvFrameAlloc() + swFrame = ffmpeg.AvFrameAlloc() + if frame == nil || swFrame == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto finish + } + + // actual decoding + for ret >= 0 { + if ret = ffmpeg.AvReadFrame(intputCtx, &pkt); ret < 0 { + break + } + + if pkt.GetStreamIndex() == videoSt.GetIndex() { + ret = decodePacket(decode, decoderCtx, frame, swFrame, &pkt, outputCtx) + } + + ffmpeg.AvPacketUnref(&pkt) + } + +finish: + if ret < 0 { + fmt.Fprintf(os.Stderr, "%s\n", ffmpeg.AvErr2str(ret)) + } + + ffmpeg.AvFormatCloseInput(&intputCtx) + + ffmpeg.AvFrameFree(&frame) + ffmpeg.AvFrameFree(&swFrame) + + ffmpeg.AvCodecFreeContext(&decoderCtx) + + ffmpeg.AvBufferUnref(&decode.hwDeviceRef) + + ffmpeg.AvIOClose(outputCtx) + + os.Exit(int(ret)) } diff --git a/examples/scaling-video/main.go b/examples/scaling-video/main.go index 7f5c756..5434810 100644 --- a/examples/scaling-video/main.go +++ b/examples/scaling-video/main.go @@ -101,7 +101,7 @@ func main() { } 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", + "ffplay -f rawvideo -pixel_format %s -video_size %dx%d %s\n", ffmpeg.AvGetPixFmtName(dstPixFmt), dstW, dstH, dstFilename) end: diff --git a/examples/transcode-aac/main.go b/examples/transcode-aac/main.go index 27aeae3..54c77b8 100644 --- a/examples/transcode-aac/main.go +++ b/examples/transcode-aac/main.go @@ -349,7 +349,7 @@ func convertSamples(inputData, convertedData **uint8, // Add converted input audio samples to the FIFO buffer for later processing. func addSamplesToFifo(fifo *ffmpeg.AVAudioFifo, convertedInputSamples **uint8, frameSize int32) int32 { - // Make the FIFO as large as it needs to be to hold both, + // Make the FIFO as large as it needs to be to hold both, // the old and the new samples. if ret := ffmpeg.AvAudioFifoRealloc(fifo, ffmpeg.AvAudioFifoSize(fifo)+frameSize); ret < 0 { fmt.Fprintf(os.Stderr, "Could not reallocate FIFO\n") diff --git a/examples/transcoding/main.go b/examples/transcoding/main.go index e9e2c67..f722bc5 100644 --- a/examples/transcoding/main.go +++ b/examples/transcoding/main.go @@ -395,7 +395,7 @@ func filterEncodeWriteFrame(ofmtCtx *ffmpeg.AVFormatContext, streamCtx []streamC ) ffmpeg.AvLog(nil, ffmpeg.AV_LOG_INFO, "Pushing decoded frame to filters\n") - // push the decoded frame into the filtergraph + // push the decoded frame into the filtergraph if ret = ffmpeg.AvBuffersrcAddFrameFlags(filter.buffersrcCtx, frame, 0); ret < 0 { ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Error while feeding the filtergraph\n") return ret diff --git a/examples/vaapi-transcode/main.go b/examples/vaapi-transcode/main.go index 7905807..3c036e4 100644 --- a/examples/vaapi-transcode/main.go +++ b/examples/vaapi-transcode/main.go @@ -1,5 +1,302 @@ package main -func main() { +/* +#include +#include +struct AVCodecContext; + +static int av_pix_fmt_none; +static int av_pix_fmt_vaapi; + +static inline void initial_pix_fmt(int x, int y) +{ + av_pix_fmt_none = x; + av_pix_fmt_vaapi = y; +} + +int get_vaapi_format(struct AVCodecContext *ctx, int *pix_fmts) +{ + const int *p; + + for (p = pix_fmts; *p != av_pix_fmt_none; p++) { + if (*p == av_pix_fmt_vaapi) + return *p; + } + + fprintf(stderr, "Unable to decode this file using VA-API.\n"); + return av_pix_fmt_none; +} + +*/ +import "C" + +import ( + "fmt" + "os" + "syscall" + + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +type streamContext struct { + ifmtCtx *ffmpeg.AVFormatContext + ofmtCtx *ffmpeg.AVFormatContext + + hwDeviceCtx *ffmpeg.AVBufferRef + + decoderCtx *ffmpeg.AVCodecContext + encoderCtx *ffmpeg.AVCodecContext + + videoStream int32 + ost *ffmpeg.AVStream + initialized bool +} + +func openInputFile(filename string, sc *streamContext) (ret int32) { + var ( + decoder *ffmpeg.AVCodec + video *ffmpeg.AVStream + ) + + if ret = ffmpeg.AvFormatOpenInput(&sc.ifmtCtx, filename, nil, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open input file '%s', Error code: %s\n", + filename, ffmpeg.AvErr2str(ret)) + return ret + } + + if ret = ffmpeg.AvFormatFindStreamInfo(sc.ifmtCtx, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot find input stream information. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + return ret + } + + if ret = ffmpeg.AvFindBestStream(sc.ifmtCtx, ffmpeg.AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot find a video stream in the input file."+ + " Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + sc.videoStream = ret + + if sc.decoderCtx = ffmpeg.AvCodecAllocContext3(decoder); sc.decoderCtx == nil { + return ffmpeg.AVERROR(syscall.ENOMEM) + } + + video = sc.ifmtCtx.GetStreams()[sc.videoStream] + if ret = ffmpeg.AvCodecParametersToContext(sc.decoderCtx, video.GetCodecpar()); ret < 0 { + fmt.Fprintf(os.Stderr, "avcodec_parameters_to_context error. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + return ret + } + + sc.decoderCtx.SetHwDeviceCtx(ffmpeg.AvBufferRef(sc.hwDeviceCtx)) + if sc.decoderCtx.GetHwDeviceCtx() == nil { + fmt.Fprintf(os.Stderr, "A hardware device reference create failed.\n") + return ffmpeg.AVERROR(syscall.ENOMEM) + } + C.initial_pix_fmt((C.int)(ffmpeg.AV_PIX_FMT_NONE), (C.int)(ffmpeg.AV_PIX_FMT_VAAPI)) + sc.decoderCtx.SetGetFormat((ffmpeg.AVCodecContextGetFormatFunc)(C.get_vaapi_format)) + + if ret = ffmpeg.AvCodecOpen2(sc.decoderCtx, decoder, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to open codec for decoding. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + } + + return 0 +} + +func encodeWrite(encPkt *ffmpeg.AVPacket, frame *ffmpeg.AVFrame, sc *streamContext) (ret int32) { + + ffmpeg.AvPacketUnref(encPkt) + + if ret = ffmpeg.AvCodecSendFrame(sc.encoderCtx, frame); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during encoding. Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto end + } + for { + if ret = ffmpeg.AvCodecReceivePacket(sc.encoderCtx, encPkt); ret != 0 { + break + } + + encPkt.SetStreamIndex(0) + ffmpeg.AvPacketRescaleTs(encPkt, sc.ifmtCtx.GetStreams()[sc.videoStream].GetTimeBase(), + sc.ofmtCtx.GetStreams()[0].GetTimeBase()) + if ret = ffmpeg.AvInterleavedWriteFrame(sc.ofmtCtx, encPkt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during writing data to output file. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + return -1 + } + } +end: + if ret == ffmpeg.AVERROR_EOF { + return 0 + } + return ffmpeg.CondExpr(ret == ffmpeg.AVERROR(syscall.EAGAIN), 0, int32(-1)) +} + +func initialEncCodecCtx(encCodec *ffmpeg.AVCodec, sc *streamContext) (ret int32) { + + // we need to ref hw_frames_ctx of decoder to initialize encoder's codec. + // Only after we get a decoded frame, can we obtain its hw_frames_ctx + sc.encoderCtx.SetHwFramesCtx(ffmpeg.AvBufferRef(sc.decoderCtx.GetHwFramesCtx())) + if sc.encoderCtx.GetHwFramesCtx() == nil { + return ffmpeg.AVERROR(syscall.ENOMEM) + } + // set AVCodecContext Parameters for encoder, here we keep them stay + // the same as decoder. + // xxx: now the sample can't handle resolution change case. + sc.encoderCtx.SetTimeBase(ffmpeg.AvInvQ(sc.decoderCtx.GetFramerate())) + sc.encoderCtx.SetPixFmt(ffmpeg.AV_PIX_FMT_VAAPI) + sc.encoderCtx.SetWidth(sc.decoderCtx.GetWidth()) + sc.encoderCtx.SetHeight(sc.decoderCtx.GetHeight()) + + if ret = ffmpeg.AvCodecOpen2(sc.encoderCtx, encCodec, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to open encode codec. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + return ret + } + sc.ost.SetTimeBase(sc.encoderCtx.GetTimeBase()) + if ret = ffmpeg.AvCodecParametersFromContext(sc.ost.GetCodecpar(), sc.encoderCtx); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to copy the stream parameters. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + + // write the stream header + if ret = ffmpeg.AvFormatWriteHeader(sc.ofmtCtx, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Error while writing stream header. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + + return 0 +} + +func decEnc(pkt *ffmpeg.AVPacket, encCodec *ffmpeg.AVCodec, sc *streamContext) (ret int32) { + var ( + frame *ffmpeg.AVFrame + ) + + if ret = ffmpeg.AvCodecSendPacket(sc.decoderCtx, pkt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during decoding. Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + + for ret >= 0 { + if frame = ffmpeg.AvFrameAlloc(); frame == nil { + return ffmpeg.AVERROR(syscall.ENOMEM) + } + + ret = ffmpeg.AvCodecReceiveFrame(sc.decoderCtx, frame) + if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF { + ffmpeg.AvFrameFree(&frame) + } else if ret < 0 { + fmt.Fprintf(os.Stderr, "Error while decoding. Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto fail + } + + if !sc.initialized { + if ret = initialEncCodecCtx(encCodec, sc); ret != 0 { + fmt.Fprintf(os.Stderr, "Initial EncodeCtx failed.\n") + goto fail + } + sc.initialized = true + } + + if ret = encodeWrite(pkt, frame, sc); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during encoding and writing.\n") + } + fail: + ffmpeg.AvFrameFree(&frame) + if ret < 0 { + return ret + } + } + return 0 +} + +func main() { + var ( + ret int32 + decPkt *ffmpeg.AVPacket + encCodec *ffmpeg.AVCodec + sc = &streamContext{} + ) + + if len(os.Args) != 4 { + fmt.Fprintf(os.Stderr, "Usage: %s \n"+ + "The output format is guessed according to the file extension.\n"+ + "\n", os.Args[0]) + os.Exit(1) + } + + if ret = ffmpeg.AvHWDeviceCtxCreate(&sc.hwDeviceCtx, ffmpeg.AV_HWDEVICE_TYPE_VAAPI, + ffmpeg.NIL, nil, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to create a VAAPI device. Error code: %s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + + if decPkt = ffmpeg.AvPacketAlloc(); decPkt == nil { + fmt.Fprintf(os.Stderr, "Failed to allocate decode packet\n") + goto end + } + + if ret = openInputFile(os.Args[1], sc); ret < 0 { + goto end + } + + if encCodec = ffmpeg.AvCodecFindEncoderByName(os.Args[2]); encCodec == nil { + fmt.Fprintf(os.Stderr, "Could not find encoder '%s'\n", os.Args[2]) + ret = -1 + goto end + } + + if ret = ffmpeg.AvFormatAllocOutputContext2(&sc.ofmtCtx, nil, ffmpeg.NIL, os.Args[3]); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to deduce output format from file extension. Error code: "+ + "%s\n", ffmpeg.AvErr2str(ret)) + goto end + } + + if sc.encoderCtx = ffmpeg.AvCodecAllocContext3(encCodec); sc.encoderCtx == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto end + } + + if ret = ffmpeg.AvIOOpen(sc.ofmtCtx.GetPbAddr(), os.Args[3], ffmpeg.AVIO_FLAG_WRITE); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open output file. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto end + } + + // read all packets and only transcoding video + for ret >= 0 { + if ret = ffmpeg.AvReadFrame(sc.ifmtCtx, decPkt); ret < 0 { + break + } + + if sc.videoStream == decPkt.GetStreamIndex() { + ret = decEnc(decPkt, encCodec, sc) + } + + ffmpeg.AvPacketUnref(decPkt) + } + + // flush decoder + ffmpeg.AvPacketUnref(decPkt) + decEnc(decPkt, encCodec, sc) + + // flush encoder + encodeWrite(decPkt, nil, sc) + + // write the trailer for output stream + ffmpeg.AvWriteTrailer(sc.ofmtCtx) + +end: + ffmpeg.AvFormatCloseInput(&sc.ifmtCtx) + ffmpeg.AvFormatCloseInput(&sc.ofmtCtx) + ffmpeg.AvCodecFreeContext(&sc.decoderCtx) + ffmpeg.AvCodecFreeContext(&sc.encoderCtx) + ffmpeg.AvBufferUnref(&sc.hwDeviceCtx) + ffmpeg.AvPacketFree(&decPkt) + os.Exit(int(ret)) } diff --git a/ffmpeg_helper.go b/ffmpeg_helper.go index a4e0899..7fce282 100644 --- a/ffmpeg_helper.go +++ b/ffmpeg_helper.go @@ -25,6 +25,33 @@ type UnsingedInteger interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr } +// CVoidPointer represents a (void*) type pointer in the C world. +type CVoidPointer any + +// VoidPointer returns a unsafe.Pointer from CVoidPointer. +func VoidPointer(a CVoidPointer) unsafe.Pointer { + if a == nil { + return nil + } + return unsafe.Pointer(reflect.ValueOf(a).Pointer()) +} + +// CVoidPointer represents a (void**) type pointer in the C world. +type CVoidPointerPointer any + +// VoidPointerPointer returns a *unsafe.Pointer from CVoidPointerPointer. +func VoidPointerPointer(a CVoidPointerPointer) *unsafe.Pointer { + if a == nil { + return nil + } + return (*unsafe.Pointer)(unsafe.Pointer(reflect.ValueOf(a).Pointer())) +} + +// FreePointer frees memory allocated in the C world. +func FreePointer(ptr CVoidPointer) { + C.free(VoidPointer(ptr)) +} + const NIL = "\\''\\" // StringCasting casts go string to c world char* with free function. @@ -34,7 +61,7 @@ func StringCasting(s string) (allocPtr *C.char, freeFunc func()) { return nil, func() {} } allocPtr = C.CString(s) - freeFunc = func() { C.free(unsafe.Pointer(allocPtr)) } + freeFunc = func() { FreePointer(allocPtr) } return allocPtr, freeFunc } @@ -96,28 +123,6 @@ func PointerOffset[U any, V Integer](ptr *U, offset V) *U { uintptr(unsafe.Sizeof(*ptr))*(uintptr(offset)))) } -// CVoidPointer represents a (void*) type pointer in the C world. -type CVoidPointer any - -// VoidPointer returns a unsafe.Pointer from CVoidPointer. -func VoidPointer(a CVoidPointer) unsafe.Pointer { - if a == nil { - return nil - } - return unsafe.Pointer(reflect.ValueOf(a).Pointer()) -} - -// CVoidPointer represents a (void**) type pointer in the C world. -type CVoidPointerPointer any - -// VoidPointerPointer returns a *unsafe.Pointer from CVoidPointerPointer. -func VoidPointerPointer(a CVoidPointerPointer) *unsafe.Pointer { - if a == nil { - return nil - } - return (*unsafe.Pointer)(unsafe.Pointer(reflect.ValueOf(a).Pointer())) -} - // CondExpr is Conditional Operator like Ternary Operator in the C world. func CondExpr[T any](cond bool, x, y T) T { if cond { @@ -126,6 +131,7 @@ func CondExpr[T any](cond bool, x, y T) T { return y } +// PlusPlus is ++ like operator. func PlusPlus[T Integer](x *T) T { defer func() { *x++ }() return *x diff --git a/swresample.go b/swresample.go index 8677a7f..bed418f 100644 --- a/swresample.go +++ b/swresample.go @@ -14,7 +14,7 @@ const ( SWR_FLAG_RESAMPLE = C.SWR_FLAG_RESAMPLE ) -// Dithering algorithms +// Dithering algorithms. type SwrDitherType = C.enum_SwrDitherType const ( @@ -33,7 +33,7 @@ const ( SWR_DITHER_NB = SwrDitherType(C.SWR_DITHER_NB) ) -// Resampling Engines +// Resampling Engines. type SwrEngine = C.enum_SwrEngine const ( @@ -42,7 +42,7 @@ const ( SWR_ENGINE_NB = SwrEngine(C.SWR_ENGINE_NB) ) -// Resampling Filter Types +// Resampling Filter Types. type SwrFilterType = C.enum_SwrFilterType const ( @@ -109,7 +109,7 @@ func SwrNextPts(s *SwrContext, pts int64) int64 { return (int64)(C.swr_next_pts((*C.struct_SwrContext)(s), (C.int64_t)(pts))) } -// SwrSetCompensation activates resampling compensation ("soft" compensation). +// SwrSetCompensation activates resampling compensation ("soft" compensation). // This function is internally called when needed in SwrNextPts(). func SwrSetCompensation(s *SwrContext, sampleDelta, compensationDistance int32) int32 { return (int32)(C.swr_set_compensation((*C.struct_SwrContext)(s),