From f00ecdba54484f44d062489d278a0c684b6deeb5 Mon Sep 17 00:00:00 2001 From: aggresss Date: Sun, 29 Oct 2023 08:11:29 +0800 Subject: [PATCH] 2023-10-29 08:11:29 CST W44D0 --- avcodec.go | 4 + avcodec_ac3_parser.go | 4 + avcodec_adts_parser.go | 4 + avcodec_avdct.go | 4 + avcodec_avfft.go | 4 + avcodec_bsf.go | 4 + avcodec_codec.go | 4 + avcodec_codec_desc.go | 4 + avcodec_codec_id.go | 4 + avcodec_codec_par.go | 4 + avcodec_d3d11va.go | 4 + avcodec_dirac.go | 4 + avcodec_dv_profile.go | 4 + avcodec_dvxa2.go | 4 + avcodec_jni.go | 4 + avcodec_mediacodec.go | 4 + avcodec_packet.go | 4 + avcodec_qsv.go | 4 + avcodec_vaapi.go | 4 + avcodec_vdpau.go | 4 + avcodec_version.go | 4 + avcodec_videotoolbox.go | 4 + avcodec_vorbis_parser.go | 4 + avcodec_xvmc.go | 4 + avdevice.go | 4 + avdevice_version.go | 4 + avfilter.go | 4 + avfilter_buffersink.go | 4 + avfilter_buffersrc.go | 4 + avfilter_version.go | 4 + avformat.go | 4 + avformat_avio.go | 4 + avformat_version.go | 4 + avutil.go | 18 +- avutil_adler32.go | 4 + avutil_aes.go | 4 + avutil_aes_ctr.go | 4 + avutil_assert.go | 4 + avutil_attributes.go | 4 + avutil_audio_fifo.go | 4 + avutil_avconfig.go | 4 + avutil_avstring.go | 4 + avutil_base64.go | 4 + avutil_blowfish.go | 4 + avutil_bprint.go | 4 + avutil_bswap.go | 4 + avutil_buffer.go | 4 + avutil_camellia.go | 4 + avutil_cast5.go | 4 + avutil_channel_layout.go | 4 + avutil_common.go | 4 + avutil_cpu.go | 4 + avutil_crc.go | 4 + avutil_des.go | 4 + avutil_dict.go | 4 + avutil_display.go | 4 + avutil_dovi_meta.go | 4 + avutil_downmix_info.go | 4 + avutil_encryption_info.go | 4 + avutil_error.go | 4 + avutil_eval.go | 4 + avutil_ffversion.go | 4 + avutil_fifo.go | 4 + avutil_file.go | 4 + avutil_film_grain_params.go | 4 + avutil_frame.go | 4 + avutil_hash.go | 4 + avutil_hdr_dynamic_metadata.go | 4 + avutil_hmac.go | 4 + avutil_hwcontext.go | 4 + avutil_hwcontext_cuda.go | 4 + avutil_hwcontext_d3d11va.go | 4 + avutil_hwcontext_drm.go | 4 + avutil_hwcontext_dxva2.go | 4 + avutil_hwcontext_mediacodec.go | 4 + avutil_hwcontext_opencl.go | 4 + avutil_hwcontext_qsv.go | 4 + avutil_hwcontext_vaapi.go | 4 + avutil_hwcontext_vdpau.go | 4 + avutil_hwcontext_videotoolbox.go | 4 + avutil_hwcontext_vulkan.go | 4 + avutil_imgutils.go | 4 + avutil_intfloat.go | 4 + avutil_intreadwrite.go | 4 + avutil_lfg.go | 4 + avutil_log.go | 4 + avutil_lzo.go | 4 + avutil_macros.go | 4 + avutil_mastering_display_metadata.go | 4 + avutil_mathematics.go | 24 +- avutil_md5.go | 4 + avutil_mem.go | 4 + avutil_motion_vector.go | 4 + avutil_murmur3.go | 4 + avutil_opt.go | 69 ++-- avutil_parseutils.go | 4 + avutil_pixdesc.go | 4 + avutil_pixelutils.go | 4 + avutil_pixfmt.go | 4 + avutil_random_seed.go | 4 + avutil_rational.go | 4 + avutil_rc4.go | 4 + avutil_replaygain.go | 4 + avutil_ripemd.go | 4 + avutil_samplefmt.go | 4 + avutil_sha.go | 4 + avutil_sha512.go | 4 + avutil_spherical.go | 4 + avutil_stereo3d.go | 4 + avutil_tea.go | 4 + avutil_threadmessage.go | 4 + avutil_time.go | 4 + avutil_timecode.go | 4 + avutil_timestamp.go | 4 + avutil_tree.go | 4 + avutil_twofish.go | 4 + avutil_tx.go | 4 + avutil_version.go | 4 + avutil_video_enc_params.go | 4 + avutil_xtea.go | 4 + examples/avio-reading/main.go | 1 + examples/filtering-audio/main.go | 4 +- examples/filtering-video/main.go | 19 +- examples/http-multiclient/main.go | 2 +- examples/hw-decode/main.go | 2 +- examples/muxing/main.go | 564 ++++++++++++++++++++++++++- examples/remuxing/main.go | 147 ++++++- examples/resampling-audio/main.go | 183 ++++++++- examples/transcoding/main.go | 556 +++++++++++++++++++++++++- examples/vaapi-encode/main.go | 202 +++++++++- ffmpeg.go | 4 + ffmpeg_helper.go | 17 + postproc.go | 4 + postproc_version.go | 4 + swresample.go | 8 +- swresample_version.go | 4 + swscale.go | 4 + swscale_version.go | 4 + 138 files changed, 2244 insertions(+), 64 deletions(-) diff --git a/avcodec.go b/avcodec.go index 8cf6b19..027468c 100644 --- a/avcodec.go +++ b/avcodec.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_ac3_parser.go b/avcodec_ac3_parser.go index 11b47ab..b6526f5 100644 --- a/avcodec_ac3_parser.go +++ b/avcodec_ac3_parser.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_adts_parser.go b/avcodec_adts_parser.go index 2aee069..4dd1879 100644 --- a/avcodec_adts_parser.go +++ b/avcodec_adts_parser.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_avdct.go b/avcodec_avdct.go index 2e9cb5e..113a166 100644 --- a/avcodec_avdct.go +++ b/avcodec_avdct.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_avfft.go b/avcodec_avfft.go index 6ae5892..317bed9 100644 --- a/avcodec_avfft.go +++ b/avcodec_avfft.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_bsf.go b/avcodec_bsf.go index 5d30831..461e285 100644 --- a/avcodec_bsf.go +++ b/avcodec_bsf.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_codec.go b/avcodec_codec.go index 935aef2..8949dbb 100644 --- a/avcodec_codec.go +++ b/avcodec_codec.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_codec_desc.go b/avcodec_codec_desc.go index 8874b28..db218d9 100644 --- a/avcodec_codec_desc.go +++ b/avcodec_codec_desc.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_codec_id.go b/avcodec_codec_id.go index da4a1da..688a4d4 100644 --- a/avcodec_codec_id.go +++ b/avcodec_codec_id.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_codec_par.go b/avcodec_codec_par.go index da2a706..83c1a6b 100644 --- a/avcodec_codec_par.go +++ b/avcodec_codec_par.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_d3d11va.go b/avcodec_d3d11va.go index b9b6374..6b491cc 100644 --- a/avcodec_d3d11va.go +++ b/avcodec_d3d11va.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_d3d11va package ffmpeg diff --git a/avcodec_dirac.go b/avcodec_dirac.go index 664e34f..a158ac3 100644 --- a/avcodec_dirac.go +++ b/avcodec_dirac.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_dv_profile.go b/avcodec_dv_profile.go index c97bdad..6749a78 100644 --- a/avcodec_dv_profile.go +++ b/avcodec_dv_profile.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_dvxa2.go b/avcodec_dvxa2.go index b358ee4..84b018a 100644 --- a/avcodec_dvxa2.go +++ b/avcodec_dvxa2.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_dvxa2 package ffmpeg diff --git a/avcodec_jni.go b/avcodec_jni.go index 74d612c..8543a69 100644 --- a/avcodec_jni.go +++ b/avcodec_jni.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_mediacodec.go b/avcodec_mediacodec.go index 8fb1596..fbafde5 100644 --- a/avcodec_mediacodec.go +++ b/avcodec_mediacodec.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_mediacodec package ffmpeg diff --git a/avcodec_packet.go b/avcodec_packet.go index 89fe2a0..1db44e2 100644 --- a/avcodec_packet.go +++ b/avcodec_packet.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_qsv.go b/avcodec_qsv.go index e52ed67..b31e46f 100644 --- a/avcodec_qsv.go +++ b/avcodec_qsv.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_qsv package ffmpeg diff --git a/avcodec_vaapi.go b/avcodec_vaapi.go index 0326ac8..104c6e7 100644 --- a/avcodec_vaapi.go +++ b/avcodec_vaapi.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_vaapi package ffmpeg diff --git a/avcodec_vdpau.go b/avcodec_vdpau.go index 94669f3..5bb62b4 100644 --- a/avcodec_vdpau.go +++ b/avcodec_vdpau.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_vdpau package ffmpeg diff --git a/avcodec_version.go b/avcodec_version.go index f57d2ee..6881b30 100644 --- a/avcodec_version.go +++ b/avcodec_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_videotoolbox.go b/avcodec_videotoolbox.go index 8d736e7..302638c 100644 --- a/avcodec_videotoolbox.go +++ b/avcodec_videotoolbox.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_videotoolbox package ffmpeg diff --git a/avcodec_vorbis_parser.go b/avcodec_vorbis_parser.go index d38366e..557f462 100644 --- a/avcodec_vorbis_parser.go +++ b/avcodec_vorbis_parser.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avcodec_xvmc.go b/avcodec_xvmc.go index 255093e..2f9ca10 100644 --- a/avcodec_xvmc.go +++ b/avcodec_xvmc.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_xvmc package ffmpeg diff --git a/avdevice.go b/avdevice.go index 468dcdd..81a158b 100644 --- a/avdevice.go +++ b/avdevice.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avdevice_version.go b/avdevice_version.go index aa20ddf..3c5e68f 100644 --- a/avdevice_version.go +++ b/avdevice_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avfilter.go b/avfilter.go index b2b68ae..f77b6da 100644 --- a/avfilter.go +++ b/avfilter.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avfilter_buffersink.go b/avfilter_buffersink.go index 8927575..516a6d3 100644 --- a/avfilter_buffersink.go +++ b/avfilter_buffersink.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avfilter_buffersrc.go b/avfilter_buffersrc.go index 5a0adb4..8834852 100644 --- a/avfilter_buffersrc.go +++ b/avfilter_buffersrc.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avfilter_version.go b/avfilter_version.go index 01454e9..78acd45 100644 --- a/avfilter_version.go +++ b/avfilter_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avformat.go b/avformat.go index 8d97741..3068c02 100644 --- a/avformat.go +++ b/avformat.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avformat_avio.go b/avformat_avio.go index e24ca22..571617c 100644 --- a/avformat_avio.go +++ b/avformat_avio.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avformat_version.go b/avformat_version.go index 551256d..ac454f6 100644 --- a/avformat_version.go +++ b/avformat_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil.go b/avutil.go index ccff964..bf0bc6c 100644 --- a/avutil.go +++ b/avutil.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* @@ -88,8 +92,18 @@ func AvGetPictureTypeChar(pictType AVPictureType) string { } // AvXIfNull returns x default pointer in case p is NULL. -func AvXIfNull(p, x unsafe.Pointer) unsafe.Pointer { - return C.av_x_if_null(p, x) +func AvXIfNull[T any](p, x *T) *T { + if p != nil { + return p + } + return x +} + +func AvStringIfNull(p, x string) string { + if len(p) != 0 { + return p + } + return x } // AvIntListLengthForSize computes the length of an integer list. diff --git a/avutil_adler32.go b/avutil_adler32.go index 135d069..b25d810 100644 --- a/avutil_adler32.go +++ b/avutil_adler32.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_aes.go b/avutil_aes.go index cc637e1..b84068a 100644 --- a/avutil_aes.go +++ b/avutil_aes.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_aes_ctr.go b/avutil_aes_ctr.go index 1621678..4da7b8f 100644 --- a/avutil_aes_ctr.go +++ b/avutil_aes_ctr.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_assert.go b/avutil_assert.go index 31cf479..ee6609d 100644 --- a/avutil_assert.go +++ b/avutil_assert.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_attributes.go b/avutil_attributes.go index dd4ed7d..7956fd2 100644 --- a/avutil_attributes.go +++ b/avutil_attributes.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_audio_fifo.go b/avutil_audio_fifo.go index ab0f8c2..275df69 100644 --- a/avutil_audio_fifo.go +++ b/avutil_audio_fifo.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_avconfig.go b/avutil_avconfig.go index c0caca4..39372bb 100644 --- a/avutil_avconfig.go +++ b/avutil_avconfig.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_avstring.go b/avutil_avstring.go index 25068a5..f4872e1 100644 --- a/avutil_avstring.go +++ b/avutil_avstring.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_base64.go b/avutil_base64.go index 72742a4..3de06f2 100644 --- a/avutil_base64.go +++ b/avutil_base64.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_blowfish.go b/avutil_blowfish.go index 3fcabef..c041763 100644 --- a/avutil_blowfish.go +++ b/avutil_blowfish.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_bprint.go b/avutil_bprint.go index 252e089..0f0c41a 100644 --- a/avutil_bprint.go +++ b/avutil_bprint.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_bswap.go b/avutil_bswap.go index ab595a8..9c182e2 100644 --- a/avutil_bswap.go +++ b/avutil_bswap.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_buffer.go b/avutil_buffer.go index f8cdfe2..74474a9 100644 --- a/avutil_buffer.go +++ b/avutil_buffer.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_camellia.go b/avutil_camellia.go index 6565de6..84d69e0 100644 --- a/avutil_camellia.go +++ b/avutil_camellia.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_cast5.go b/avutil_cast5.go index 74f947f..72768a9 100644 --- a/avutil_cast5.go +++ b/avutil_cast5.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_channel_layout.go b/avutil_channel_layout.go index b5f732e..54e39f3 100644 --- a/avutil_channel_layout.go +++ b/avutil_channel_layout.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_common.go b/avutil_common.go index a598673..2b5f758 100644 --- a/avutil_common.go +++ b/avutil_common.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_cpu.go b/avutil_cpu.go index b36a148..010a683 100644 --- a/avutil_cpu.go +++ b/avutil_cpu.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_crc.go b/avutil_crc.go index 48c3255..0dbfeed 100644 --- a/avutil_crc.go +++ b/avutil_crc.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_des.go b/avutil_des.go index 074621b..9fec1fb 100644 --- a/avutil_des.go +++ b/avutil_des.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_dict.go b/avutil_dict.go index f5fe55d..7cc1799 100644 --- a/avutil_dict.go +++ b/avutil_dict.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_display.go b/avutil_display.go index ed06f76..747ef8b 100644 --- a/avutil_display.go +++ b/avutil_display.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_dovi_meta.go b/avutil_dovi_meta.go index f328d36..0283197 100644 --- a/avutil_dovi_meta.go +++ b/avutil_dovi_meta.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_downmix_info.go b/avutil_downmix_info.go index bf5e191..eb53048 100644 --- a/avutil_downmix_info.go +++ b/avutil_downmix_info.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_encryption_info.go b/avutil_encryption_info.go index aece3b4..0db0565 100644 --- a/avutil_encryption_info.go +++ b/avutil_encryption_info.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_error.go b/avutil_error.go index 179b988..6705943 100644 --- a/avutil_error.go +++ b/avutil_error.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_eval.go b/avutil_eval.go index 3601fad..4aed02e 100644 --- a/avutil_eval.go +++ b/avutil_eval.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_ffversion.go b/avutil_ffversion.go index ebc4e48..52ae300 100644 --- a/avutil_ffversion.go +++ b/avutil_ffversion.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_fifo.go b/avutil_fifo.go index 73f6342..e918d7c 100644 --- a/avutil_fifo.go +++ b/avutil_fifo.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_file.go b/avutil_file.go index 3d5ce51..4cc0a9e 100644 --- a/avutil_file.go +++ b/avutil_file.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_film_grain_params.go b/avutil_film_grain_params.go index 40be9ab..992516b 100644 --- a/avutil_film_grain_params.go +++ b/avutil_film_grain_params.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_frame.go b/avutil_frame.go index 7c640f4..9d0d0a9 100644 --- a/avutil_frame.go +++ b/avutil_frame.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_hash.go b/avutil_hash.go index 0984fe6..34111e4 100644 --- a/avutil_hash.go +++ b/avutil_hash.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_hdr_dynamic_metadata.go b/avutil_hdr_dynamic_metadata.go index 914a198..8d46a04 100644 --- a/avutil_hdr_dynamic_metadata.go +++ b/avutil_hdr_dynamic_metadata.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_hmac.go b/avutil_hmac.go index 6e23e3b..c7aa574 100644 --- a/avutil_hmac.go +++ b/avutil_hmac.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_hwcontext.go b/avutil_hwcontext.go index 124c76f..fece950 100644 --- a/avutil_hwcontext.go +++ b/avutil_hwcontext.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_hwcontext_cuda.go b/avutil_hwcontext_cuda.go index d0d8837..f957055 100644 --- a/avutil_hwcontext_cuda.go +++ b/avutil_hwcontext_cuda.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_cuda package ffmpeg diff --git a/avutil_hwcontext_d3d11va.go b/avutil_hwcontext_d3d11va.go index 8aac854..f544653 100644 --- a/avutil_hwcontext_d3d11va.go +++ b/avutil_hwcontext_d3d11va.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_d3d11va package ffmpeg diff --git a/avutil_hwcontext_drm.go b/avutil_hwcontext_drm.go index f060674..dd3bae9 100644 --- a/avutil_hwcontext_drm.go +++ b/avutil_hwcontext_drm.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_drm package ffmpeg diff --git a/avutil_hwcontext_dxva2.go b/avutil_hwcontext_dxva2.go index 2237ee7..3f9baf5 100644 --- a/avutil_hwcontext_dxva2.go +++ b/avutil_hwcontext_dxva2.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_dxva2 package ffmpeg diff --git a/avutil_hwcontext_mediacodec.go b/avutil_hwcontext_mediacodec.go index ffcf44d..d3be0b2 100644 --- a/avutil_hwcontext_mediacodec.go +++ b/avutil_hwcontext_mediacodec.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_mediacodec package ffmpeg diff --git a/avutil_hwcontext_opencl.go b/avutil_hwcontext_opencl.go index 25fae00..df9cacf 100644 --- a/avutil_hwcontext_opencl.go +++ b/avutil_hwcontext_opencl.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_opencl package ffmpeg diff --git a/avutil_hwcontext_qsv.go b/avutil_hwcontext_qsv.go index 808c606..b2a7a5f 100644 --- a/avutil_hwcontext_qsv.go +++ b/avutil_hwcontext_qsv.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_qsv package ffmpeg diff --git a/avutil_hwcontext_vaapi.go b/avutil_hwcontext_vaapi.go index e2f1d95..8db0cd9 100644 --- a/avutil_hwcontext_vaapi.go +++ b/avutil_hwcontext_vaapi.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_vaapi package ffmpeg diff --git a/avutil_hwcontext_vdpau.go b/avutil_hwcontext_vdpau.go index fe82005..238b778 100644 --- a/avutil_hwcontext_vdpau.go +++ b/avutil_hwcontext_vdpau.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_vdpau package ffmpeg diff --git a/avutil_hwcontext_videotoolbox.go b/avutil_hwcontext_videotoolbox.go index 5e2e5ba..4738aee 100644 --- a/avutil_hwcontext_videotoolbox.go +++ b/avutil_hwcontext_videotoolbox.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_videotoolbox package ffmpeg diff --git a/avutil_hwcontext_vulkan.go b/avutil_hwcontext_vulkan.go index f977688..ac56139 100644 --- a/avutil_hwcontext_vulkan.go +++ b/avutil_hwcontext_vulkan.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + //go:build ffmpeg_hw_vulkan package ffmpeg diff --git a/avutil_imgutils.go b/avutil_imgutils.go index 5620d71..1db1824 100644 --- a/avutil_imgutils.go +++ b/avutil_imgutils.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_intfloat.go b/avutil_intfloat.go index c4d3433..0ec0eda 100644 --- a/avutil_intfloat.go +++ b/avutil_intfloat.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_intreadwrite.go b/avutil_intreadwrite.go index 550dc4d..98b736e 100644 --- a/avutil_intreadwrite.go +++ b/avutil_intreadwrite.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_lfg.go b/avutil_lfg.go index 90682b0..554e85c 100644 --- a/avutil_lfg.go +++ b/avutil_lfg.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_log.go b/avutil_log.go index 2215fde..4656243 100644 --- a/avutil_log.go +++ b/avutil_log.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_lzo.go b/avutil_lzo.go index 6e97237..0847c5a 100644 --- a/avutil_lzo.go +++ b/avutil_lzo.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_macros.go b/avutil_macros.go index 77cf3e5..a96f83d 100644 --- a/avutil_macros.go +++ b/avutil_macros.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_mastering_display_metadata.go b/avutil_mastering_display_metadata.go index 319252e..7695aea 100644 --- a/avutil_mastering_display_metadata.go +++ b/avutil_mastering_display_metadata.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_mathematics.go b/avutil_mathematics.go index 9b7e5b7..91acb07 100644 --- a/avutil_mathematics.go +++ b/avutil_mathematics.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* @@ -17,29 +21,29 @@ const ( ) // AvGcd computes the greatest common divisor of two integer operands. -func AvGcd(a, b int64) int64 { - return (int64)(C.av_gcd((C.int64_t)(a), (C.int64_t)(b))) +func AvGcd[T Integer](a, b T) T { + return (T)(C.av_gcd((C.int64_t)(a), (C.int64_t)(b))) } // AvRescale rescale a 64-bit integer with rounding to nearest. -func AvRescale(a, b, c int64) int64 { - return (int64)(C.av_rescale((C.int64_t)(a), (C.int64_t)(b), (C.int64_t)(c))) +func AvRescale[T Integer](a, b, c T) T { + return (T)(C.av_rescale((C.int64_t)(a), (C.int64_t)(b), (C.int64_t)(c))) } // AvRescaleRnd rescales a 64-bit integer with specified rounding. -func AvRescaleRnd(a, b, c int64, rnd AVRounding) int64 { - return (int64)(C.av_rescale_rnd((C.int64_t)(a), (C.int64_t)(b), (C.int64_t)(c), +func AvRescaleRnd[T Integer](a, b, c T, rnd AVRounding) T { + return (T)(C.av_rescale_rnd((C.int64_t)(a), (C.int64_t)(b), (C.int64_t)(c), (C.enum_AVRounding)(rnd))) } // AvRescaleQ rescales a 64-bit integer by 2 rational numbers. -func AvRescaleQ(a int64, bq, cq AVRational) int64 { - return (int64)(C.av_rescale_q((C.int64_t)(a), (C.struct_AVRational)(bq), (C.struct_AVRational)(cq))) +func AvRescaleQ[T Integer](a T, bq, cq AVRational) T { + return (T)(C.av_rescale_q((C.int64_t)(a), (C.struct_AVRational)(bq), (C.struct_AVRational)(cq))) } // AvRescaleQRnd rescales a 64-bit integer by 2 rational numbers with specified rounding. -func AvRescaleQRnd(a int64, bq, cq AVRational, rnd AVRounding) int64 { - return (int64)(C.av_rescale_q_rnd((C.int64_t)(a), (C.struct_AVRational)(bq), (C.struct_AVRational)(cq), +func AvRescaleQRnd[T Integer](a T, bq, cq AVRational, rnd AVRounding) T { + return (T)(C.av_rescale_q_rnd((C.int64_t)(a), (C.struct_AVRational)(bq), (C.struct_AVRational)(cq), (C.enum_AVRounding)(rnd))) } diff --git a/avutil_md5.go b/avutil_md5.go index 0616715..f433603 100644 --- a/avutil_md5.go +++ b/avutil_md5.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_mem.go b/avutil_mem.go index a503a13..267f7cf 100644 --- a/avutil_mem.go +++ b/avutil_mem.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_motion_vector.go b/avutil_motion_vector.go index d41a8ed..5afb4ee 100644 --- a/avutil_motion_vector.go +++ b/avutil_motion_vector.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_murmur3.go b/avutil_murmur3.go index 65d05dc..e923b00 100644 --- a/avutil_murmur3.go +++ b/avutil_murmur3.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_opt.go b/avutil_opt.go index 7f5e3da..7b2fa1b 100644 --- a/avutil_opt.go +++ b/avutil_opt.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* @@ -522,77 +526,88 @@ func AvOptSet(obj CVoidPointer, name string, val string, searchFlags int32) int3 defer nameFunc() valPtr, valFunc := StringCasting(val) defer valFunc() - return (int32)(C.av_opt_set(VoidPointer(obj), (*C.char)(namePtr), (*C.char)(valPtr), (C.int)(searchFlags))) + return (int32)(C.av_opt_set(VoidPointer(obj), (*C.char)(namePtr), + (*C.char)(valPtr), (C.int)(searchFlags))) } // AvOptSetInt -func AvOptSetInt(obj CVoidPointer, name string, val int64, searchFlags int32) int32 { +func AvOptSetInt[T Integer](obj CVoidPointer, name string, val T, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_int(VoidPointer(obj), (*C.char)(namePtr), (C.int64_t)(val), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_int(VoidPointer(obj), (*C.char)(namePtr), + (C.int64_t)(val), (C.int)(searchFlags))) } // AvOptSetDouble func AvOptSetDouble(obj CVoidPointer, name string, val float64, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_double(VoidPointer(obj), (*C.char)(namePtr), (C.double)(val), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_double(VoidPointer(obj), (*C.char)(namePtr), + (C.double)(val), (C.int)(searchFlags))) } // AvOptSetQ func AvOptSetQ(obj CVoidPointer, name string, val AVRational, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_q(VoidPointer(obj), (*C.char)(namePtr), (C.struct_AVRational)(val), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_q(VoidPointer(obj), (*C.char)(namePtr), + (C.struct_AVRational)(val), (C.int)(searchFlags))) } // AvOptSetBin -func AvOptSetBin(obj CVoidPointer, name string, val *uint8, size int32, searchFlags int32) int32 { +func AvOptSetBin[U any, V Integer](obj CVoidPointer, name string, val *U, size V, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_bin(VoidPointer(obj), (*C.char)(namePtr), (*C.uint8_t)(val), (C.int)(size), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_bin(VoidPointer(obj), (*C.char)(namePtr), + (*C.uint8_t)(unsafe.Pointer(val)), (C.int)(size), (C.int)(searchFlags))) } // AvOptSetImageSize func AvOptSetImageSize(obj CVoidPointer, name string, w, h int32, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_image_size(VoidPointer(obj), (*C.char)(namePtr), (C.int)(w), (C.int)(h), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_image_size(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(w), (C.int)(h), (C.int)(searchFlags))) } // AvOptSetPixelFmt func AvOptSetPixelFmt(obj CVoidPointer, name string, fmt AVPixelFormat, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_pixel_fmt(VoidPointer(obj), (*C.char)(namePtr), (C.enum_AVPixelFormat)(fmt), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_pixel_fmt(VoidPointer(obj), (*C.char)(namePtr), + (C.enum_AVPixelFormat)(fmt), (C.int)(searchFlags))) } // AvOptSetSampleFmt func AvOptSetSampleFmt(obj CVoidPointer, name string, fmt AVSampleFormat, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_sample_fmt(VoidPointer(obj), (*C.char)(namePtr), (C.enum_AVSampleFormat)(fmt), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_sample_fmt(VoidPointer(obj), (*C.char)(namePtr), + (C.enum_AVSampleFormat)(fmt), (C.int)(searchFlags))) } // AvOptSetVideoRate func AvOptSetVideoRate(obj CVoidPointer, name string, val AVRational, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_video_rate(VoidPointer(obj), (*C.char)(namePtr), (C.struct_AVRational)(val), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_video_rate(VoidPointer(obj), (*C.char)(namePtr), + (C.struct_AVRational)(val), (C.int)(searchFlags))) } // AvOptSetChannelLayout func AvOptSetChannelLayout(obj CVoidPointer, name string, chLayout int64, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_channel_layout(VoidPointer(obj), (*C.char)(namePtr), (C.int64_t)(chLayout), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_channel_layout(VoidPointer(obj), (*C.char)(namePtr), + (C.int64_t)(chLayout), (C.int)(searchFlags))) } // AvOptSetDictVal func AvOptSetDictVal(obj CVoidPointer, name string, val *AVDictionary, searchFlags int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_set_dict_val(VoidPointer(obj), (*C.char)(namePtr), (*C.struct_AVDictionary)(val), (C.int)(searchFlags))) + return (int32)(C.av_opt_set_dict_val(VoidPointer(obj), (*C.char)(namePtr), + (*C.struct_AVDictionary)(val), (C.int)(searchFlags))) } // AvOptSetIntList sets a binary option to an integer list. @@ -621,64 +636,72 @@ func AvOptGet(obj CVoidPointer, name string, searchFlags int32, outVal **uint8) func AvOptGetInt(obj CVoidPointer, name string, searchFlags int32, outVal *int64) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_int(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.int64_t)(outVal))) + return (int32)(C.av_opt_get_int(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.int64_t)(outVal))) } // AvOptGetDouble func AvOptGetDouble(obj CVoidPointer, name string, searchFlags int32, outVal *float64) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_double(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.double)(outVal))) + return (int32)(C.av_opt_get_double(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.double)(outVal))) } // AvOptGetQ func AvOptGetQ(obj CVoidPointer, name string, searchFlags int32, outVal *AVRational) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_q(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.struct_AVRational)(outVal))) + return (int32)(C.av_opt_get_q(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.struct_AVRational)(outVal))) } // AvOptGetImageSize func AvOptGetImageSize(obj CVoidPointer, name string, searchFlags int32, wOut, hOut *int32) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_image_size(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.int)(wOut), (*C.int)(hOut))) + return (int32)(C.av_opt_get_image_size(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.int)(wOut), (*C.int)(hOut))) } // AvOptGetPixelFmt func AvOptGetPixelFmt(obj CVoidPointer, name string, searchFlags int32, outFmt *AVPixelFormat) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_pixel_fmt(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.enum_AVPixelFormat)(outFmt))) + return (int32)(C.av_opt_get_pixel_fmt(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.enum_AVPixelFormat)(outFmt))) } // AvOptGetSampleFmt func AvOptGetSampleFmt(obj CVoidPointer, name string, searchFlags int32, outFmt *AVSampleFormat) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_sample_fmt(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.enum_AVSampleFormat)(outFmt))) + return (int32)(C.av_opt_get_sample_fmt(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.enum_AVSampleFormat)(outFmt))) } // AvOptGetVideoRate func AvOptGetVideoRate(obj CVoidPointer, name string, searchFlags int32, outVal *AVRational) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_video_rate(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.struct_AVRational)(outVal))) + return (int32)(C.av_opt_get_video_rate(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.struct_AVRational)(outVal))) } // AvOptGetChannelLayout func AvOptGetChannelLayout(obj CVoidPointer, name string, searchFlags int32, outVal *int64) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_channel_layout(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), (*C.int64_t)(outVal))) + return (int32)(C.av_opt_get_channel_layout(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (*C.int64_t)(outVal))) } // AvOptGetDictVal func AvOptGetDictVal(obj CVoidPointer, name string, searchFlags int32, outVal **AVDictionary) int32 { namePtr, nameFunc := StringCasting(name) defer nameFunc() - return (int32)(C.av_opt_get_dict_val(VoidPointer(obj), (*C.char)(namePtr), (C.int)(searchFlags), - (**C.struct_AVDictionary)(unsafe.Pointer(outVal)))) + return (int32)(C.av_opt_get_dict_val(VoidPointer(obj), (*C.char)(namePtr), + (C.int)(searchFlags), (**C.struct_AVDictionary)(unsafe.Pointer(outVal)))) } // AvOptPtr gets a pointer to the requested field in a struct. diff --git a/avutil_parseutils.go b/avutil_parseutils.go index 5a45c25..db0f471 100644 --- a/avutil_parseutils.go +++ b/avutil_parseutils.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_pixdesc.go b/avutil_pixdesc.go index a991c36..18ba3ed 100644 --- a/avutil_pixdesc.go +++ b/avutil_pixdesc.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_pixelutils.go b/avutil_pixelutils.go index 2ecd471..44bb8b7 100644 --- a/avutil_pixelutils.go +++ b/avutil_pixelutils.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_pixfmt.go b/avutil_pixfmt.go index 4c14a6d..1ebe779 100644 --- a/avutil_pixfmt.go +++ b/avutil_pixfmt.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_random_seed.go b/avutil_random_seed.go index c02dd44..7a3d6f0 100644 --- a/avutil_random_seed.go +++ b/avutil_random_seed.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_rational.go b/avutil_rational.go index e4d9d3d..df361ac 100644 --- a/avutil_rational.go +++ b/avutil_rational.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_rc4.go b/avutil_rc4.go index 0ac3fc0..f9d7c52 100644 --- a/avutil_rc4.go +++ b/avutil_rc4.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_replaygain.go b/avutil_replaygain.go index f733d02..fa5e244 100644 --- a/avutil_replaygain.go +++ b/avutil_replaygain.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_ripemd.go b/avutil_ripemd.go index 863a622..1883cd8 100644 --- a/avutil_ripemd.go +++ b/avutil_ripemd.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_samplefmt.go b/avutil_samplefmt.go index e53653d..3ac53ad 100644 --- a/avutil_samplefmt.go +++ b/avutil_samplefmt.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_sha.go b/avutil_sha.go index 7d4a41c..2f65030 100644 --- a/avutil_sha.go +++ b/avutil_sha.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_sha512.go b/avutil_sha512.go index 6a53d7f..d86ce73 100644 --- a/avutil_sha512.go +++ b/avutil_sha512.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_spherical.go b/avutil_spherical.go index c6cf290..36b8601 100644 --- a/avutil_spherical.go +++ b/avutil_spherical.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_stereo3d.go b/avutil_stereo3d.go index 96d6675..762e364 100644 --- a/avutil_stereo3d.go +++ b/avutil_stereo3d.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_tea.go b/avutil_tea.go index f91bf63..56a61aa 100644 --- a/avutil_tea.go +++ b/avutil_tea.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_threadmessage.go b/avutil_threadmessage.go index 778df7c..5b75b46 100644 --- a/avutil_threadmessage.go +++ b/avutil_threadmessage.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_time.go b/avutil_time.go index 77b4959..8fa0145 100644 --- a/avutil_time.go +++ b/avutil_time.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_timecode.go b/avutil_timecode.go index 312e3c9..f66e764 100644 --- a/avutil_timecode.go +++ b/avutil_timecode.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_timestamp.go b/avutil_timestamp.go index 25d7d5d..4a5829d 100644 --- a/avutil_timestamp.go +++ b/avutil_timestamp.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_tree.go b/avutil_tree.go index 9ad8735..dacb5a9 100644 --- a/avutil_tree.go +++ b/avutil_tree.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_twofish.go b/avutil_twofish.go index e998dc7..04b2be1 100644 --- a/avutil_twofish.go +++ b/avutil_twofish.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_tx.go b/avutil_tx.go index 4e489d6..4b96bf1 100644 --- a/avutil_tx.go +++ b/avutil_tx.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_version.go b/avutil_version.go index bdb67d1..6ddddb3 100644 --- a/avutil_version.go +++ b/avutil_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_video_enc_params.go b/avutil_video_enc_params.go index e85acaa..e479363 100644 --- a/avutil_video_enc_params.go +++ b/avutil_video_enc_params.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/avutil_xtea.go b/avutil_xtea.go index 07bf1a1..dc51dd2 100644 --- a/avutil_xtea.go +++ b/avutil_xtea.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/examples/avio-reading/main.go b/examples/avio-reading/main.go index 453592b..e661ccc 100644 --- a/examples/avio-reading/main.go +++ b/examples/avio-reading/main.go @@ -2,6 +2,7 @@ package main /* #include +#include int readPacket(void* opaque, uint8_t *buf, int bufSize); diff --git a/examples/filtering-audio/main.go b/examples/filtering-audio/main.go index 74b165d..61435c8 100644 --- a/examples/filtering-audio/main.go +++ b/examples/filtering-audio/main.go @@ -6,7 +6,7 @@ import ( "syscall" "unsafe" - "github.com/qrtc/ffmpeg-dev-go" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" ) var ( @@ -151,7 +151,7 @@ func initFilters(decCtx *ffmpeg.AVCodecContext, fmtCtx *ffmpeg.AVFormatContext, args = ffmpeg.AvGetChannelLayoutString(-1, outlink.GetChannelLayout()) ffmpeg.AvLog(nil, ffmpeg.AV_LOG_INFO, "Output: srate:%dHz fmt:%s chlayout:%s\n", outlink.GetSampleRate(), - ffmpeg.AvGetSampleFmtName(outlink.GetFormat()), + ffmpeg.AvStringIfNull(ffmpeg.AvGetSampleFmtName(outlink.GetFormat()), "?"), args) end: diff --git a/examples/filtering-video/main.go b/examples/filtering-video/main.go index 9bcc459..a391974 100644 --- a/examples/filtering-video/main.go +++ b/examples/filtering-video/main.go @@ -1,22 +1,5 @@ package main -/* -#include -#include - -static void putcs(uint16_t *p, int n) -{ - const uint16_t *p_end = p + n; - while (p < p_end) { - fputc(*p & 0xff, stdout); - fputc(*p>>8 & 0xff, stdout); - p++; - } - fflush(stdout); -} -*/ -import "C" - import ( "fmt" "os" @@ -24,7 +7,7 @@ import ( "time" "unsafe" - "github.com/qrtc/ffmpeg-dev-go" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" ) var ( diff --git a/examples/http-multiclient/main.go b/examples/http-multiclient/main.go index a31102c..a6cee8e 100644 --- a/examples/http-multiclient/main.go +++ b/examples/http-multiclient/main.go @@ -47,7 +47,7 @@ func processClient(client *ffmpeg.AVIOContext, inUri string) { ffmpeg.AvLog(client, ffmpeg.AV_LOG_TRACE, "resource: %s", resourceStr) replyCode = ffmpeg.AVERROR_HTTP_NOT_FOUND } - if ret = ffmpeg.AvOptSetInt(client, "reply_code", int64(replyCode), ffmpeg.AV_OPT_SEARCH_CHILDREN); ret < 0 { + if ret = ffmpeg.AvOptSetInt(client, "reply_code", replyCode, ffmpeg.AV_OPT_SEARCH_CHILDREN); ret < 0 { ffmpeg.AvLog(client, ffmpeg.AV_LOG_ERROR, "Failed to set reply_code: %s.\n", ffmpeg.AvErr2str(ret)) goto end } diff --git a/examples/hw-decode/main.go b/examples/hw-decode/main.go index 5e0bd0e..377f1a1 100644 --- a/examples/hw-decode/main.go +++ b/examples/hw-decode/main.go @@ -40,7 +40,7 @@ import ( "syscall" "unsafe" - "github.com/qrtc/ffmpeg-dev-go" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" ) var ( diff --git a/examples/muxing/main.go b/examples/muxing/main.go index 7905807..de70e85 100644 --- a/examples/muxing/main.go +++ b/examples/muxing/main.go @@ -1,5 +1,567 @@ package main -func main() { +import ( + "fmt" + "math" + "os" + "syscall" + "unsafe" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +const ( + STREAM_DURATION = 10.0 + STREAM_FRAME_RATE = 25 // 25 images/s + STREAM_PIX_FMT = ffmpeg.AV_PIX_FMT_YUV420P //default pix_fmt + SCALE_FLAGS = ffmpeg.SWS_BICUBIC +) + +type outputStream struct { + st *ffmpeg.AVStream + enc *ffmpeg.AVCodecContext + + // pts of the next frame that will be generated + nextPts int64 + samplesCount int32 + frame *ffmpeg.AVFrame + tmpFrame *ffmpeg.AVFrame + + t, tincr, tincr2 float32 + + swsCtx *ffmpeg.SwsContext + swrCtx *ffmpeg.SwrContext +} + +func logPacket(fmtCtx *ffmpeg.AVFormatContext, pkt *ffmpeg.AVPacket) { + timeBase := fmtCtx.GetStreams()[pkt.GetStreamIndex()].GetTimeBaseAddr() + fmt.Fprintf(os.Stdout, "pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n", + ffmpeg.AvTs2str(pkt.GetPts()), ffmpeg.AvTs2timestr(pkt.GetPts(), timeBase), + ffmpeg.AvTs2str(pkt.GetDts()), ffmpeg.AvTs2timestr(pkt.GetDts(), timeBase), + ffmpeg.AvTs2str(pkt.GetDuration()), ffmpeg.AvTs2timestr(pkt.GetDuration(), timeBase), + pkt.GetStreamIndex()) +} + +func writeFrame(fmtCtx *ffmpeg.AVFormatContext, c *ffmpeg.AVCodecContext, st *ffmpeg.AVStream, frame *ffmpeg.AVFrame) bool { + var ( + ret int32 + ) + // send the frame to the encoder + if ret = ffmpeg.AvCodecSendFrame(c, frame); ret < 0 { + fmt.Fprintf(os.Stderr, "Error sending a frame to the encoder: %s\n", + ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + + for ret >= 0 { + var pkt ffmpeg.AVPacket + + ret = ffmpeg.AvCodecReceivePacket(c, &pkt) + if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF { + break + } else if ret < 0 { + fmt.Fprintf(os.Stderr, "Error encoding a frame: %s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + + // rescale output packet timestamp values from codec to stream timebase + ffmpeg.AvPacketRescaleTs(&pkt, c.GetTimeBase(), st.GetTimeBase()) + pkt.SetStreamIndex(st.GetIndex()) + + // Write the compressed frame to the media file. + logPacket(fmtCtx, &pkt) + ret = ffmpeg.AvInterleavedWriteFrame(fmtCtx, &pkt) + ffmpeg.AvPacketUnref(&pkt) + if ret < 0 { + fmt.Fprintf(os.Stderr, "Error while writing output packet: %s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + } + + return ret == ffmpeg.AVERROR_EOF +} + +// Add an output stream. +func addStream(ost *outputStream, oc *ffmpeg.AVFormatContext, codecId ffmpeg.AVCodecID) (codec *ffmpeg.AVCodec) { + var ( + c *ffmpeg.AVCodecContext + ) + if codec = ffmpeg.AvCodecFindEncoder(codecId); codec == nil { + fmt.Fprintf(os.Stderr, "Could not find encoder for '%s'\n", ffmpeg.AvCodecGetName(codecId)) + os.Exit(1) + } + + if ost.st = ffmpeg.AvFormatNewStream(oc, nil); ost.st == nil { + fmt.Fprintf(os.Stderr, "Could not allocate stream\n") + os.Exit(1) + } + ost.st.SetId(int32(oc.GetNbStreams() - 1)) + if c = ffmpeg.AvCodecAllocContext3(codec); c == nil { + fmt.Fprintf(os.Stderr, "Could not alloc an encoding context\n") + os.Exit(1) + } + ost.enc = c + + switch codec.GetType() { + case ffmpeg.AVMEDIA_TYPE_AUDIO: + c.SetSampleFmt(ffmpeg.CondExpr(len(codec.GetSampleFmts()) > 0, + codec.GetSampleFmts()[0], ffmpeg.AV_SAMPLE_FMT_FLTP)) + c.SetBitRate(64_000) + c.SetSampleRate(44_100) + if len(codec.GetSupportedSamplerates()) > 0 { + c.SetSampleRate(codec.GetSupportedSamplerates()[0]) + for _, sr := range codec.GetSupportedSamplerates() { + if sr == 44_100 { + c.SetSampleRate(44_100) + } + } + } + c.SetChannels(ffmpeg.AvGetChannelLayoutNbChannels(c.GetChannelLayout())) + c.SetChannelLayout(ffmpeg.AV_CH_LAYOUT_STEREO) + if len(codec.GetChannelLayouts()) > 0 { + c.SetChannelLayout(codec.GetChannelLayouts()[0]) + for _, cl := range codec.GetChannelLayouts() { + if cl == ffmpeg.AV_CH_LAYOUT_STEREO { + c.SetChannelLayout(ffmpeg.AV_CH_LAYOUT_STEREO) + } + } + } + c.SetChannels(ffmpeg.AvGetChannelLayoutNbChannels(c.GetChannelLayout())) + ost.st.SetTimeBase(ffmpeg.AvMakeQ(1, c.GetSampleRate())) + + case ffmpeg.AVMEDIA_TYPE_VIDEO: + c.SetCodecId(codecId) + + c.SetBitRate(4_000_000) + // Resolution must be a multiple of two. + c.SetWidth(352) + c.SetHeight(288) + // timebase: This is the fundamental unit of time (in seconds) in terms + // of which frame timestamps are represented. For fixed-fps content, + // timebase should be 1/framerate and timestamp increments should be + // identical to 1. + ost.st.SetTimeBase(ffmpeg.AvMakeQ(1, STREAM_FRAME_RATE)) + c.SetTimeBase(ost.st.GetTimeBase()) + + c.SetGopSize(12) // emit one intra frame every twelve frames at most + c.SetPixFmt(STREAM_PIX_FMT) + if c.GetCodecId() == ffmpeg.AV_CODEC_ID_MPEG2VIDEO { + // just for testing, we also add B-frames + c.SetMaxBFrames(2) + } + if c.GetCodecId() == ffmpeg.AV_CODEC_ID_MPEG1VIDEO { + // Needed to avoid using macroblocks in which some coeffs overflow. + // This does not happen with normal video, it just happens here as + // the motion of the chroma plane does not match the luma plane. + c.SetMbDecision(2) + } + + default: + break + } + + if (oc.GetOformat().GetFlags() & ffmpeg.AVFMT_GLOBALHEADER) != 0 { + c.SetFlags(c.GetFlags() | ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER) + } + + return codec +} + +// ************************************************************** +// audio output + +func allocAudioFrame(sampleFmt ffmpeg.AVSampleFormat, channelLayout uint64, sampleRate int32, nbSamples int32) ( + frame *ffmpeg.AVFrame) { + if frame = ffmpeg.AvFrameAlloc(); frame == nil { + panic("Error allocating an audio frame") + } + + frame.SetFormat(sampleFmt) + frame.SetChannelLayout(channelLayout) + frame.SetSampleRate(sampleRate) + frame.SetNbSamples(nbSamples) + + if nbSamples != 0 { + if ret := ffmpeg.AvFrameGetBuffer(frame, 0); ret < 0 { + panic("Error allocating an audio buffer") + } + } + return frame +} + +func openAudio(oc *ffmpeg.AVFormatContext, codec *ffmpeg.AVCodec, ost *outputStream, optArg *ffmpeg.AVDictionary) { + var ( + c *ffmpeg.AVCodecContext + opt *ffmpeg.AVDictionary + nbSamples int32 + ) + c = ost.enc + + // open it + ffmpeg.AvDictCopy(&opt, optArg, 0) + ret := ffmpeg.AvCodecOpen2(c, codec, &opt) + ffmpeg.AvDictFree(&opt) + if ret < 0 { + panic(fmt.Sprintf("Could not open audio codec: %s", ffmpeg.AvErr2str(ret))) + } + + // init signal generator + ost.t = 0 + ost.tincr = float32(2 * math.Pi * 110.0 / float64(c.GetSampleRate())) + // increment frequency by 110 Hz per second + ost.tincr2 = float32(2 * math.Pi * 110.0 / float64(c.GetSampleRate()) / float64(c.GetSampleRate())) + + if (c.GetCodec().GetCapabilities() & ffmpeg.AV_CODEC_CAP_VARIABLE_FRAME_SIZE) != 0 { + nbSamples = 10_000 + } else { + nbSamples = c.GetFrameSize() + } + ost.frame = allocAudioFrame(c.GetSampleFmt(), c.GetChannelLayout(), + c.GetSampleRate(), nbSamples) + ost.tmpFrame = allocAudioFrame(ffmpeg.AV_SAMPLE_FMT_S16, c.GetChannelLayout(), + c.GetSampleRate(), nbSamples) + + // copy the stream parameters to the muxer + if ret = ffmpeg.AvCodecParametersFromContext(ost.st.GetCodecpar(), c); ret < 0 { + panic("Could not copy the stream parameters") + } + + // create resampler context + if ost.swrCtx = ffmpeg.SwrAlloc(); ost.swrCtx == nil { + panic("Could not allocate resampler context") + } + + // set options + ffmpeg.AvOptSetInt(ost.swrCtx, "in_channel_count", c.GetChannels(), 0) + ffmpeg.AvOptSetInt(ost.swrCtx, "in_sample_rate", c.GetSampleRate(), 0) + ffmpeg.AvOptSetSampleFmt(ost.swrCtx, "in_sample_fmt", ffmpeg.AV_SAMPLE_FMT_S16, 0) + ffmpeg.AvOptSetInt(ost.swrCtx, "out_channel_count", c.GetChannels(), 0) + ffmpeg.AvOptSetInt(ost.swrCtx, "out_sample_rate", c.GetSampleRate(), 0) + ffmpeg.AvOptSetSampleFmt(ost.swrCtx, "out_sample_fmt", c.GetSampleFmt(), 0) + + // initialize the resampling context + if ret = ffmpeg.SwrInit(ost.swrCtx); ret < 0 { + panic("Failed to initialize the resampling context") + } +} + +// Prepare a 16 bit dummy audio frame of 'frame_size' samples and +// 'nb_channels' channels. +func getAudioFrame(ost *outputStream) (frame *ffmpeg.AVFrame) { + frame = ost.tmpFrame + data := unsafe.Slice((*int16)(unsafe.Pointer(frame.GetData()[0])), + frame.GetNbSamples()*ost.enc.GetChannels()) + + if ffmpeg.AvCompareTs(ost.nextPts, ost.enc.GetTimeBase(), STREAM_DURATION, ffmpeg.AvMakeQ(1, 1)) > 0 { + return nil + } + idx := 0 + for j := 0; j < int(frame.GetNbSamples()); j++ { + v := (int16)(math.Sin(float64(ost.t)) * 10_000) + for i := 0; i < int(ost.enc.GetChannels()); i++ { + data[idx] = v + idx++ + ost.t += ost.tincr + ost.tincr += ost.tincr2 + } + } + frame.SetPts(ost.nextPts) + ost.nextPts += int64(frame.GetNbSamples()) + + return frame +} + +// encode one audio frame and send it to the muxer +// return 1 when encoding is finished, 0 otherwise +func writeAudioFrame(oc *ffmpeg.AVFormatContext, ost *outputStream) bool { + var ( + c *ffmpeg.AVCodecContext + frame *ffmpeg.AVFrame + ret int32 + dstNbSamples int32 + ) + c = ost.enc + + frame = getAudioFrame(ost) + + if frame != nil { + // convert samples from native format to destination codec format, using the resampler */ + // compute destination number of samples + dstNbSamples = ffmpeg.AvRescaleRnd(ffmpeg.SwrGetDelay(ost.swrCtx, c.GetSampleRate())+frame.GetNbSamples(), + c.GetSampleRate(), c.GetSampleRate(), ffmpeg.AV_ROUND_UP) + ffmpeg.AvAssert0(dstNbSamples == frame.GetNbSamples()) + + // 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 ret = ffmpeg.AvFrameMakeWritable(ost.frame); ret < 0 { + panic("Make frame writeable failed") + } + + if ret = ffmpeg.SwrConvert(ost.swrCtx, + &ost.frame.GetData()[0], dstNbSamples, + &frame.GetData()[0], frame.GetNbSamples()); ret < 0 { + panic("Error while converting") + } + frame = ost.frame + + frame.SetPts(ffmpeg.AvRescaleQ(int64(ost.samplesCount), ffmpeg.AvMakeQ(1, c.GetSampleRate()), c.GetTimeBase())) + ost.samplesCount += dstNbSamples + } + + return writeFrame(oc, c, ost.st, frame) +} + +// ************************************************************** +// video output + +func allocPicture(pixFmt ffmpeg.AVPixelFormat, width, height int32) (picture *ffmpeg.AVFrame) { + if picture = ffmpeg.AvFrameAlloc(); picture == nil { + return nil + } + + picture.SetFormat(pixFmt) + picture.SetWidth(width) + picture.SetHeight(height) + + // allocate the buffers for the frame data + if ret := ffmpeg.AvFrameGetBuffer(picture, 0); ret < 0 { + panic("Could not allocate frame data.") + } + return picture +} + +func openVideo(oc *ffmpeg.AVFormatContext, codec *ffmpeg.AVCodec, + ost *outputStream, optArg *ffmpeg.AVDictionary) { + var ( + ret int32 + c = ost.enc + opt *ffmpeg.AVDictionary + ) + + ffmpeg.AvDictCopy(&opt, optArg, 0) + + // open the codec + ret = ffmpeg.AvCodecOpen2(c, codec, &opt) + ffmpeg.AvDictFree(&opt) + if ret < 0 { + panic(fmt.Sprintf("Could not video audio codec: %s", ffmpeg.AvErr2str(ret))) + } + + // allocate and init a re-usable frame + if ost.frame = allocPicture(c.GetPixFmt(), c.GetWidth(), c.GetHeight()); ost.frame == nil { + panic("Could not allocate video frame") + } + + // If the output format is not YUV420P, then a temporary YUV420P + // picture is needed too. It is then converted to the required + // output format. + ost.tmpFrame = nil + if c.GetPixFmt() != ffmpeg.AV_PIX_FMT_YUV420P { + ost.tmpFrame = allocPicture(ffmpeg.AV_PIX_FMT_YUV420P, c.GetWidth(), c.GetHeight()) + if ost.tmpFrame == nil { + panic("Could not allocate temporary picture") + } + } + + // copy the stream parameters to the muxer + if ret = ffmpeg.AvCodecParametersFromContext(ost.st.GetCodecpar(), c); ret < 0 { + panic("Could not copy the stream parameters") + } +} + +// Prepare a dummy image. +func fillYuvImage(pict *ffmpeg.AVFrame, frameIndex int32, width, height int32) { + var ( + data = ffmpeg.SliceSlice(&pict.GetData()[0], 3, width*height) + linesize = pict.GetLinesize() + i = frameIndex + ) + + // Y + for y := int32(0); y < height; y++ { + for x := int32(0); x < width; x++ { + data[0][y*linesize[0]+x] = uint8(x + y + i*3) + } + } + // Cb and Cr + for y := int32(0); y < height/2; y++ { + for x := int32(0); x < width/2; x++ { + data[1][y*linesize[1]+x] = uint8(128 + y + i*2) + data[2][y*linesize[2]+x] = uint8(64 + x + i*5) + } + } +} + +func getVideoFrame(ost *outputStream) *ffmpeg.AVFrame { + c := ost.enc + + // check if we want to generate more frames + if ffmpeg.AvCompareTs(ost.nextPts, c.GetTimeBase(), STREAM_DURATION, ffmpeg.AvMakeQ(1, 1)) > 0 { + return nil + } + + // 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") + } + + if c.GetPixFmt() != ffmpeg.AV_PIX_FMT_YUV420P { + // as we only generate a YUV420P picture, we must convert it + // to the codec pixel format if needed + if ost.swsCtx == nil { + ost.swsCtx = ffmpeg.SwsGetContext(c.GetWidth(), c.GetHeight(), + ffmpeg.AV_PIX_FMT_YUV420P, + c.GetWidth(), c.GetHeight(), + c.GetPixFmt(), + SCALE_FLAGS, nil, nil, nil) + if ost.swsCtx == nil { + panic("Could not initialize the conversion context") + } + } + fillYuvImage(ost.tmpFrame, int32(ost.nextPts), c.GetWidth(), c.GetHeight()) + ffmpeg.SwsScale(ost.swsCtx, ost.tmpFrame.GetData(), + ost.tmpFrame.GetLinesize(), 0, c.GetHeight(), ost.frame.GetData(), + ost.frame.GetLinesize()) + } else { + fillYuvImage(ost.frame, int32(ost.nextPts), c.GetWidth(), c.GetHeight()) + } + + ost.frame.SetPts(ffmpeg.PlusPlus(&ost.nextPts)) + + return ost.frame +} + +// encode one video frame and send it to the muxer +// return 1 when encoding is finished, 0 otherwise +func writeVideoFrame(oc *ffmpeg.AVFormatContext, ost *outputStream) bool { + return writeFrame(oc, ost.enc, ost.st, getVideoFrame(ost)) +} + +func closeStream(oc *ffmpeg.AVFormatContext, ost *outputStream) { + ffmpeg.AvCodecFreeContext(&ost.enc) + ffmpeg.AvFrameFree(&ost.frame) + ffmpeg.AvFrameFree(&ost.tmpFrame) + ffmpeg.SwsFreeContext(ost.swsCtx) + ffmpeg.SwrFree(&ost.swrCtx) +} + +// ************************************************************** +// media file output + +func main() { + var ( + videoSt, audioSt outputStream + videoCodec, audioCodec *ffmpeg.AVCodec + haveVideo, haveAudio bool + encodeVideo, encodeAudio bool + opt *ffmpeg.AVDictionary + ret int32 + _fmt *ffmpeg.AVOutputFormat + oc *ffmpeg.AVFormatContext + ) + + if len(os.Args) < 2 { + fmt.Fprintf(os.Stdout, "usage: %s output_file\n"+ + "API example program to output a media file with libavformat.\n"+ + "This program generates a synthetic audio and video stream, encodes and\n"+ + "muxes them into a file named output_file.\n"+ + "The output format is automatically guessed according to the file extension.\n"+ + "Raw images can also be output by using '%%d' in the filename.\n"+ + "\n", os.Args[0]) + os.Exit(1) + } + + filename := os.Args[1] + for i := 2; i+1 < len(os.Args); i += 2 { + if os.Args[i] == "-flags" || os.Args[i] == "-fflags" { + ffmpeg.AvDictSet(&opt, os.Args[i][1:], os.Args[i+1], 0) + } + } + + // allocate the output media context + ffmpeg.AvFormatAllocOutputContext2(&oc, nil, ffmpeg.NIL, filename) + if oc == nil { + fmt.Fprintf(os.Stderr, "Could not deduce output format from file extension: using MPEG.\n") + ffmpeg.AvFormatAllocOutputContext2(&oc, nil, "mpeg", filename) + } + if oc == nil { + panic("Allocate the output media context failed") + } + + _fmt = oc.GetOformat() + + // Add the audio and video streams using the default format codecs + // and initialize the codecs. + if _fmt.GetVideoCodec() != ffmpeg.AV_CODEC_ID_NONE { + videoCodec = addStream(&videoSt, oc, _fmt.GetVideoCodec()) + haveVideo = true + encodeVideo = true + } + if _fmt.GetAudioCodec() != ffmpeg.AV_CODEC_ID_NONE { + audioCodec = addStream(&audioSt, oc, _fmt.GetAudioCodec()) + haveAudio = true + encodeAudio = true + } + + // Now that all the parameters are set, we can open the audio and + // video codecs and allocate the necessary encode buffers. + if haveVideo { + openVideo(oc, videoCodec, &videoSt, opt) + } + if haveAudio { + openAudio(oc, audioCodec, &audioSt, opt) + } + + ffmpeg.AvDumpFormat(oc, 0, filename, 1) + + // open the output file, if needed + if (_fmt.GetFlags() & ffmpeg.AVFMT_NOFILE) == 0 { + if ret = ffmpeg.AvIOOpen(oc.GetPbAddr(), filename, ffmpeg.AVIO_FLAG_WRITE); ret < 0 { + fmt.Fprintf(os.Stderr, "Could not open '%s': %s\n", filename, ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + } + + // Write the stream header, if any. + if ret = ffmpeg.AvFormatWriteHeader(oc, &opt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error occurred when opening output file: %s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + + for encodeVideo || encodeAudio { + // select the stream to encode + if encodeVideo && + (!encodeAudio || ffmpeg.AvCompareTs(videoSt.nextPts, videoSt.enc.GetTimeBase(), + audioSt.nextPts, audioSt.enc.GetTimeBase()) <= 0) { + encodeVideo = !writeVideoFrame(oc, &videoSt) + } else { + encodeAudio = !writeAudioFrame(oc, &audioSt) + } + } + + // Write the trailer, if any. The trailer must be written before you + // close the CodecContexts open when you wrote the header; otherwise + // AvWriteTrailer() may try to use memory that was freed on + // AvCodecClose(). + ffmpeg.AvWriteTrailer(oc) + + // Close each codec. + if haveVideo { + closeStream(oc, &videoSt) + } + if haveAudio { + closeStream(oc, &audioSt) + } + + if (_fmt.GetFlags() & ffmpeg.AVFMT_NOFILE) == 0 { + // Close the output file. + ffmpeg.AvIOClosep(oc.GetPbAddr()) + } + + // free the stream + ffmpeg.AvFormatFreeContext(oc) + + os.Exit(0) } diff --git a/examples/remuxing/main.go b/examples/remuxing/main.go index 7905807..1dfe8d5 100644 --- a/examples/remuxing/main.go +++ b/examples/remuxing/main.go @@ -1,5 +1,150 @@ package main -func main() { +import ( + "fmt" + "os" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +func logPacket(fmtCtx *ffmpeg.AVFormatContext, pkt *ffmpeg.AVPacket, tag string) { + timeBase := fmtCtx.GetStreams()[pkt.GetStreamIndex()].GetTimeBaseAddr() + fmt.Fprintf(os.Stdout, "%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n", + tag, + ffmpeg.AvTs2str(pkt.GetPts()), ffmpeg.AvTs2timestr(pkt.GetPts(), timeBase), + ffmpeg.AvTs2str(pkt.GetDts()), ffmpeg.AvTs2timestr(pkt.GetDts(), timeBase), + ffmpeg.AvTs2str(pkt.GetDuration()), ffmpeg.AvTs2timestr(pkt.GetDuration(), timeBase), + pkt.GetStreamIndex()) +} + +func main() { + var ( + ofmt *ffmpeg.AVOutputFormat + ifmtCtx, ofmtCtx *ffmpeg.AVFormatContext + pkt ffmpeg.AVPacket + ret int32 + streamMapping []int32 + streamIndex int32 + ) + + if len(os.Args) < 3 { + fmt.Fprintf(os.Stderr, "usage: %s input output\n"+ + "API example program to remux a media file with libavformat and libavcodec.\n"+ + "The output format is guessed according to the file extension.\n"+ + "\n", os.Args[0]) + os.Exit(1) + } + + inFilename := os.Args[1] + outFilename := os.Args[2] + + if ret = ffmpeg.AvFormatOpenInput(&ifmtCtx, inFilename, nil, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Could not open input file '%s'", inFilename) + goto end + } + if ret = ffmpeg.AvFormatFindStreamInfo(ifmtCtx, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to retrieve input stream information") + goto end + } + + ffmpeg.AvDumpFormat(ifmtCtx, 0, inFilename, 0) + + if ffmpeg.AvFormatAllocOutputContext2(&ofmtCtx, nil, ffmpeg.NIL, outFilename); ofmtCtx == nil { + fmt.Fprintf(os.Stderr, "Could not create output context\n") + ret = ffmpeg.AVERROR_UNKNOWN + goto end + } + + streamMapping = make([]int32, ifmtCtx.GetNbStreams()) + + ofmt = ofmtCtx.GetOformat() + + for i := 0; i < len(streamMapping); i++ { + inStream := ifmtCtx.GetStreams()[i] + inCodecPar := inStream.GetCodecpar() + + if inCodecPar.GetCodecType() != ffmpeg.AVMEDIA_TYPE_AUDIO && + inCodecPar.GetCodecType() != ffmpeg.AVMEDIA_TYPE_VIDEO && + inCodecPar.GetCodecType() != ffmpeg.AVMEDIA_TYPE_SUBTITLE { + streamMapping[i] = -1 + continue + } + + streamMapping[i] = streamIndex + streamIndex++ + + outStream := ffmpeg.AvFormatNewStream(ofmtCtx, nil) + if outStream == nil { + fmt.Fprintf(os.Stderr, "Failed allocating output stream\n") + ret = ffmpeg.AVERROR_UNKNOWN + goto end + } + + if ret = ffmpeg.AvCodecParametersCopy(outStream.GetCodecpar(), inCodecPar); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to copy codec parameters\n") + goto end + } + outStream.GetCodecpar().SetCodecTag(0) + } + ffmpeg.AvDumpFormat(ofmtCtx, 0, outFilename, 1) + + if (ofmt.GetFlags() & ffmpeg.AVFMT_NOFILE) == 0 { + if ret = ffmpeg.AvIOOpen(ofmtCtx.GetPbAddr(), outFilename, ffmpeg.AVIO_FLAG_WRITE); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Could not open output file '%s'", outFilename) + goto end + } + } + + if ret = ffmpeg.AvFormatWriteHeader(ofmtCtx, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Error occurred when opening output file\n") + goto end + } + + for { + var inStream, outStream *ffmpeg.AVStream + + if ret = ffmpeg.AvReadFrame(ifmtCtx, &pkt); ret < 0 { + break + } + + inStream = ifmtCtx.GetStreams()[pkt.GetStreamIndex()] + if int(pkt.GetStreamIndex()) >= len(streamMapping) || + streamMapping[pkt.GetStreamIndex()] < 0 { + ffmpeg.AvPacketUnref(&pkt) + continue + } + + pkt.SetStreamIndex(streamMapping[pkt.GetStreamIndex()]) + outStream = ofmtCtx.GetStreams()[pkt.GetStreamIndex()] + logPacket(ifmtCtx, &pkt, "in") + + // copy packet + pkt.SetPts(ffmpeg.AvRescaleQRnd(pkt.GetPts(), inStream.GetTimeBase(), outStream.GetTimeBase(), + ffmpeg.AV_ROUND_NEAR_INF|ffmpeg.AV_ROUND_PASS_MINMAX)) + pkt.SetDts(ffmpeg.AvRescaleQRnd(pkt.GetDts(), inStream.GetTimeBase(), outStream.GetTimeBase(), + ffmpeg.AV_ROUND_NEAR_INF|ffmpeg.AV_ROUND_PASS_MINMAX)) + pkt.SetDuration(ffmpeg.AvRescaleQ(pkt.GetDuration(), inStream.GetTimeBase(), outStream.GetTimeBase())) + pkt.SetPos(-1) + logPacket(ofmtCtx, &pkt, "out") + + if ret = ffmpeg.AvInterleavedWriteFrame(ofmtCtx, &pkt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error muxing packet\n") + break + } + ffmpeg.AvPacketUnref(&pkt) + } + ffmpeg.AvWriteTrailer(ofmtCtx) +end: + // close output + ffmpeg.AvFormatCloseInput(&ifmtCtx) + if ofmtCtx != nil && (ofmt.GetFlags()&ffmpeg.AVFMT_NOFILE) == 0 { + ffmpeg.AvIOClosep(ofmtCtx.GetPbAddr()) + } + ffmpeg.AvFormatFreeContext(ofmtCtx) + + if ret < 0 && ret != ffmpeg.AVERROR_EOF { + fmt.Fprintf(os.Stderr, "Error occurred: %s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + os.Exit(0) } diff --git a/examples/resampling-audio/main.go b/examples/resampling-audio/main.go index 7905807..ef9d58f 100644 --- a/examples/resampling-audio/main.go +++ b/examples/resampling-audio/main.go @@ -1,5 +1,186 @@ package main -func main() { +import ( + "fmt" + "math" + "os" + "syscall" + "unsafe" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +func getFormatFromSampleFmt(sampleFmt ffmpeg.AVSampleFormat) (string, int32) { + sampleFmtEntry := []struct { + sampleFmt ffmpeg.AVSampleFormat + fmtBe string + fmtLe string + }{ + {ffmpeg.AV_SAMPLE_FMT_U8, "u8", "u8"}, + {ffmpeg.AV_SAMPLE_FMT_S16, "s16be", "s16le"}, + {ffmpeg.AV_SAMPLE_FMT_S32, "s32be", "s32le"}, + {ffmpeg.AV_SAMPLE_FMT_FLT, "f32be", "f32le"}, + {ffmpeg.AV_SAMPLE_FMT_DBL, "f64be", "f64le"}, + } + + for _, entry := range sampleFmtEntry { + if sampleFmt == entry.sampleFmt { + return ffmpeg.AV_NE(entry.fmtBe, entry.fmtLe), 0 + } + } + + fmt.Fprintf(os.Stderr, "sample format %s is not supported as output format\n", + ffmpeg.AvGetSampleFmtName(sampleFmt)) + return ffmpeg.NIL, -1 +} + +// Fill dst buffer with nb_samples, generated starting from t. +func fileSamples(dst []float64, nbSamples, nbChannels, sampleRate int32, t *float64) { + var ( + tincr = 1.0 / float64(sampleRate) + c = 2 * math.Pi * 440.0 + ) + + // generate sin tone with 440Hz frequency and duplicated channels + for i := int32(0); i < nbSamples; i++ { + dst[i*nbChannels] = math.Sin(c * (*t)) + for j := int32(1); j < nbChannels; j++ { + dst[i*nbChannels+j] = dst[i*nbChannels] + } + *t += tincr + } +} + +func main() { + var ( + srcRate, dstRate int32 = 48000, 44100 + srcData, dstData **uint8 + srcNbChannels, dstNbChannels int32 + srcLinesize, dstLinesize int32 + srcNbSamples, dstNbSamples int32 = 1024, 0 + maxDstNbSamples int32 + srcChLayout, dstChLayout = ffmpeg.AV_CH_LAYOUT_STEREO, ffmpeg.AV_CH_LAYOUT_SURROUND + srcSampleFmt, dstSampleFmt = ffmpeg.AV_SAMPLE_FMT_DBL, ffmpeg.AV_SAMPLE_FMT_S16 + swrCtx *ffmpeg.SwrContext + ret int32 + t float64 + dstBufsize int32 + _fmt string + ) + + if len(os.Args) != 2 { + fmt.Fprintf(os.Stdout, "Usage: %s output_file\n"+ + "API example program to show how to resample an audio stream with libswresample.\n"+ + "This program generates a series of audio frames, resamples them to a specified "+ + "output format and rate and saves them to an output file named output_file.\n", + os.Args[0]) + os.Exit(1) + } + dstFilename := os.Args[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 destination file %s\n", dstFilename) + os.Exit(1) + } + + // create resampler context + if swrCtx = ffmpeg.SwrAlloc(); swrCtx == nil { + fmt.Fprintf(os.Stderr, "Could not allocate resampler context\n") + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto end + } + + // set options + ffmpeg.AvOptSetInt(swrCtx, "in_channel_layout", srcChLayout, 0) + ffmpeg.AvOptSetInt(swrCtx, "in_sample_rate", srcRate, 0) + ffmpeg.AvOptSetSampleFmt(swrCtx, "in_sample_fmt", srcSampleFmt, 0) + + ffmpeg.AvOptSetInt(swrCtx, "out_channel_layout", dstChLayout, 0) + ffmpeg.AvOptSetInt(swrCtx, "out_sample_rate", dstRate, 0) + ffmpeg.AvOptSetSampleFmt(swrCtx, "out_sample_fmt", dstSampleFmt, 0) + + // initialize the resampling context + if ret = ffmpeg.SwrInit(swrCtx); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize the resampling context\n") + goto end + } + + // allocate source and destination samples buffers + srcNbChannels = ffmpeg.AvGetChannelLayoutNbChannels(srcChLayout) + if ret = ffmpeg.AvSamplesAllocArrayAndSamples(&srcData, &srcLinesize, srcNbChannels, + srcNbSamples, srcSampleFmt, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Could not allocate source samples\n") + goto end + } + + // compute the number of converted samples: buffering is avoided + // ensuring that the output buffer will contain at least all the + // converted input samples + dstNbSamples = ffmpeg.AvRescaleRnd(srcNbSamples, dstRate, srcRate, ffmpeg.AV_ROUND_UP) + maxDstNbSamples = dstNbSamples + + // buffer is going to be directly written to a rawaudio file, no alignment + dstNbChannels = ffmpeg.AvGetChannelLayoutNbChannels(dstChLayout) + if ret = ffmpeg.AvSamplesAllocArrayAndSamples(&dstData, &dstLinesize, dstNbChannels, + dstNbSamples, dstSampleFmt, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Could not allocate destination samples\n") + goto end + } + + for ok := true; ok; ok = (t < 10) { + // generate synthetic audio + fileSamples(unsafe.Slice((*float64)(unsafe.Pointer(*srcData)), srcNbSamples*srcNbChannels), + srcNbSamples, srcNbChannels, srcRate, &t) + + //compute destination number of samples + dstNbSamples = ffmpeg.AvRescaleRnd(ffmpeg.SwrGetDelay(swrCtx, srcRate)+srcNbSamples, + dstRate, srcRate, ffmpeg.AV_ROUND_UP) + if dstNbSamples > maxDstNbSamples { + ffmpeg.AvFreep(dstData) + if ret = ffmpeg.AvSamplesAlloc(dstData, &dstLinesize, dstNbChannels, + dstNbSamples, dstSampleFmt, 1); ret < 0 { + break + } + maxDstNbSamples = dstNbSamples + } + + // convert to destination format + if ret = ffmpeg.SwrConvert(swrCtx, dstData, dstNbSamples, srcData, srcNbSamples); ret < 0 { + fmt.Fprintf(os.Stderr, "Error while converting\n") + goto end + } + if dstBufsize = ffmpeg.AvSamplesGetBufferSize(&dstLinesize, dstNbChannels, + ret, dstSampleFmt, 1); dstBufsize < 0 { + fmt.Fprintf(os.Stderr, "Could not get sample buffer size\n") + goto end + } + fmt.Fprintf(os.Stdout, "t:%f in:%d out:%d\n", t, srcNbSamples, ret) + dstFile.Write(ffmpeg.SliceSlice(dstData, 1, dstBufsize)[0]) + } + + if _fmt, ret = getFormatFromSampleFmt(dstSampleFmt); ret < 0 { + goto end + } + fmt.Fprintf(os.Stderr, "Resampling succeeded. Play the output file with the command:\n"+ + "ffplay -f %s -channel_layout %d -channels %d -ar %d %s\n", + _fmt, dstChLayout, dstNbChannels, dstRate, dstFilename) + +end: + dstFile.Close() + + if srcData != nil { + ffmpeg.AvFreep(srcData) + } + ffmpeg.AvFreep(&srcData) + + if dstData != nil { + ffmpeg.AvFreep(dstData) + } + ffmpeg.AvFreep(&dstData) + + ffmpeg.SwrFree(&swrCtx) + if ret < 0 { + os.Exit(1) + } } diff --git a/examples/transcoding/main.go b/examples/transcoding/main.go index 7905807..e9e2c67 100644 --- a/examples/transcoding/main.go +++ b/examples/transcoding/main.go @@ -1,5 +1,559 @@ package main -func main() { +import ( + "fmt" + "os" + "syscall" + "unsafe" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +type filteringContext struct { + buffersinkCtx *ffmpeg.AVFilterContext + buffersrcCtx *ffmpeg.AVFilterContext + filterGraph *ffmpeg.AVFilterGraph + encPkt *ffmpeg.AVPacket + filteredFrame *ffmpeg.AVFrame +} + +type streamContext struct { + decCtx *ffmpeg.AVCodecContext + encCtx *ffmpeg.AVCodecContext + decFrame *ffmpeg.AVFrame +} + +// Open an input file and the required decoder. +func openInputFile(fileName string) (ifmtCtx *ffmpeg.AVFormatContext, streamCtx []streamContext, ret int32) { + if ret = ffmpeg.AvFormatOpenInput(&ifmtCtx, fileName, nil, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot open input file\n") + return nil, nil, ret + } + + if ret = ffmpeg.AvFormatFindStreamInfo(ifmtCtx, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot find stream information\n") + return nil, nil, ret + } + + streamCtx = make([]streamContext, ifmtCtx.GetNbStreams()) + + for i := 0; i < int(ifmtCtx.GetNbStreams()); i++ { + stream := ifmtCtx.GetStreams()[i] + dec := ffmpeg.AvCodecFindDecoder(stream.GetCodecpar().GetCodecId()) + if dec == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Failed to find decoder for stream #%d\n", i) + return nil, nil, ffmpeg.AVERROR_DECODER_NOT_FOUND + } + codecCtx := ffmpeg.AvCodecAllocContext3(dec) + if codecCtx == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Failed to allocate the decoder context for stream #%d\n", i) + return nil, nil, ffmpeg.AVERROR(syscall.ENOMEM) + } + if ret = ffmpeg.AvCodecParametersToContext(codecCtx, stream.GetCodecpar()); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context "+ + "for stream #%d\n", i) + return nil, nil, ret + } + // Reencode video & audio and remux subtitles etc. + if codecCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_VIDEO || + codecCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_AUDIO { + if codecCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_VIDEO { + codecCtx.SetFramerate(ffmpeg.AvGuessFrameRate(ifmtCtx, stream, nil)) + } + // Open decoder + if ret = ffmpeg.AvCodecOpen2(codecCtx, dec, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Failed to open decoder for stream #%d\n", i) + return nil, nil, ret + } + } + streamCtx[i].decCtx = codecCtx + + if streamCtx[i].decFrame = ffmpeg.AvFrameAlloc(); streamCtx[i].decFrame == nil { + return nil, nil, ffmpeg.AVERROR(syscall.ENOMEM) + } + } + + ffmpeg.AvDumpFormat(ifmtCtx, 0, fileName, 0) + return ifmtCtx, streamCtx, 0 +} + +func openOutputFile(ifmtCtx *ffmpeg.AVFormatContext, streamCtx []streamContext, + filename string) (ofmtCtx *ffmpeg.AVFormatContext, ret int32) { + if ffmpeg.AvFormatAllocOutputContext2(&ofmtCtx, nil, ffmpeg.NIL, filename); ofmtCtx == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Could not create output context\n") + return nil, ret + } + + for i := 0; i < len(streamCtx); i++ { + outStream := ffmpeg.AvFormatNewStream(ofmtCtx, nil) + if outStream == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Failed allocating output stream\n") + return nil, ret + } + + inStream := ifmtCtx.GetStreams()[i] + decCtx := streamCtx[i].decCtx + + if decCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_VIDEO || + decCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_AUDIO { + encoder := ffmpeg.AvCodecFindEncoder(decCtx.GetCodecId()) + if encoder == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Necessary encoder not found\n") + return nil, ffmpeg.AVERROR_INVALIDDATA + } + encCtx := ffmpeg.AvCodecAllocContext3(encoder) + if encCtx == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Failed to allocate the encoder context\n") + return nil, ffmpeg.AVERROR(syscall.ENOMEM) + } + + // In this example, we transcode to same properties (picture size, + // sample rate etc.). These properties can be changed for output + // streams easily using filters + if decCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_VIDEO { + encCtx.SetHeight(decCtx.GetHeight()) + encCtx.SetWidth(decCtx.GetWidth()) + encCtx.SetSampleAspectRatio(decCtx.GetSampleAspectRatio()) + // take first format from list of supported formats + if len(encoder.GetPixFmts()) != 0 { + encCtx.SetPixFmt(encoder.GetPixFmts()[0]) + } else { + encCtx.SetPixFmt(decCtx.GetPixFmt()) + } + // video time_base can be set to whatever is handy and supported by encoder + encCtx.SetTimeBase(ffmpeg.AvInvQ(decCtx.GetFramerate())) + } else { + encCtx.SetSampleRate(decCtx.GetSampleRate()) + encCtx.SetChannelLayout(decCtx.GetChannelLayout()) + encCtx.SetChannels(ffmpeg.AvGetChannelLayoutNbChannels(encCtx.GetChannelLayout())) + // take first format from list of supported formats + encCtx.SetSampleFmt(encoder.GetSampleFmts()[0]) + encCtx.SetTimeBase(ffmpeg.AvMakeQ(1, encCtx.GetSampleRate())) + } + + if (ofmtCtx.GetOformat().GetFlags() & ffmpeg.AVFMT_GLOBALHEADER) != 0 { + encCtx.SetFlags(encCtx.GetFlags() | ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER) + } + + // Third parameter can be used to pass settings to encoder + if ret = ffmpeg.AvCodecOpen2(encCtx, encoder, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot open video encoder for stream #%d\n", i) + return nil, ret + } + if ret = ffmpeg.AvCodecParametersFromContext(outStream.GetCodecpar(), encCtx); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Failed to copy encoder parameters to output stream #%d\n", i) + return nil, ret + } + + outStream.SetTimeBase(encCtx.GetTimeBase()) + streamCtx[i].encCtx = encCtx + } else if decCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_UNKNOWN { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Elementary stream #%d is of unknown type, cannot proceed\n", i) + return nil, ffmpeg.AVERROR_INVALIDDATA + } else { + // if this stream must be remuxed + if ret = ffmpeg.AvCodecParametersCopy(outStream.GetCodecpar(), inStream.GetCodecpar()); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Copying parameters for stream #%d failed\n", i) + return nil, ret + } + outStream.SetTimeBase(inStream.GetTimeBase()) + } + } + ffmpeg.AvDumpFormat(ofmtCtx, 0, filename, 1) + + if (ofmtCtx.GetOformat().GetFlags() & ffmpeg.AVFMT_NOFILE) == 0 { + if ret = ffmpeg.AvIOOpen(ofmtCtx.GetPbAddr(), filename, ffmpeg.AVIO_FLAG_WRITE); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Could not open output file '%s'", filename) + return nil, ret + } + } + + // init muxer, write output file header + if ret = ffmpeg.AvFormatWriteHeader(ofmtCtx, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Error occurred when opening output file\n") + return nil, ret + } + + return ofmtCtx, 0 +} + +func initFilter(fctx *filteringContext, decCtx, encCtx *ffmpeg.AVCodecContext, filterSpec string) (ret int32) { + var ( + buffersrc *ffmpeg.AVFilter + buffersink *ffmpeg.AVFilter + buffersrcCtx *ffmpeg.AVFilterContext + buffersinkCtx *ffmpeg.AVFilterContext + outputs = ffmpeg.AvFilterInoutAlloc() + inputs = ffmpeg.AvFilterInoutAlloc() + filterGraph = ffmpeg.AvFilterGraphAlloc() + args string + ) + + if outputs == nil || inputs == nil || filterGraph == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto end + } + + if decCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_VIDEO { + buffersrc = ffmpeg.AvFilterGetByName("buffer") + buffersink = ffmpeg.AvFilterGetByName("buffersink") + if buffersrc == nil || buffersink == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "filtering source or sink element not found\n") + ret = ffmpeg.AVERROR_UNKNOWN + goto end + } + + args = fmt.Sprintf("video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", + decCtx.GetWidth(), decCtx.GetHeight(), decCtx.GetPixFmt(), + decCtx.GetTimeBaseAddr().GetNum(), decCtx.GetTimeBaseAddr().GetDen(), + decCtx.GetSampleAspectRatioAddr().GetNum(), decCtx.GetSampleAspectRatioAddr().GetDen()) + + if ret = ffmpeg.AvFilterGraphCreateFilter(&buffersrcCtx, buffersrc, "in", + args, nil, filterGraph); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot create buffer source\n") + goto end + } + + if ret = ffmpeg.AvFilterGraphCreateFilter(&buffersinkCtx, buffersink, "out", + ffmpeg.NIL, nil, filterGraph); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot create buffer sink\n") + goto end + } + + if ret = ffmpeg.AvOptSetBin(buffersinkCtx, "pix_fmts", + encCtx.GetPixFmtAddr(), unsafe.Sizeof(encCtx.GetPixFmt()), ffmpeg.AV_OPT_SEARCH_CHILDREN); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot set output pixel format\n") + goto end + } + } else if decCtx.GetCodecType() == ffmpeg.AVMEDIA_TYPE_AUDIO { + buffersrc = ffmpeg.AvFilterGetByName("abuffer") + buffersink = ffmpeg.AvFilterGetByName("abuffersink") + if buffersrc == nil || buffersink == nil { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "filtering source or sink element not found\n") + ret = ffmpeg.AVERROR_UNKNOWN + goto end + } + + if decCtx.GetChannelLayout() == 0 { + decCtx.SetChannelLayout(uint64(ffmpeg.AvGetDefaultChannelLayout(decCtx.GetChannels()))) + } + + args = fmt.Sprintf("time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%d", + decCtx.GetTimeBaseAddr().GetNum(), decCtx.GetTimeBaseAddr().GetDen(), decCtx.GetSampleRate(), + ffmpeg.AvGetSampleFmtName(decCtx.GetSampleFmt()), decCtx.GetChannelLayout()) + + if ret = ffmpeg.AvFilterGraphCreateFilter(&buffersrcCtx, buffersrc, "in", + args, nil, filterGraph); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot create audio buffer source\n") + goto end + } + + if ret = ffmpeg.AvFilterGraphCreateFilter(&buffersinkCtx, buffersink, "out", + ffmpeg.NIL, nil, filterGraph); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot create audio buffer sink\n") + goto end + } + + if ret = ffmpeg.AvOptSetBin(buffersinkCtx, "sample_fmts", + encCtx.GetSampleFmtAddr(), unsafe.Sizeof(encCtx.GetSampleFmt()), ffmpeg.AV_OPT_SEARCH_CHILDREN); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot set output sample format\n") + goto end + } + + if ret = ffmpeg.AvOptSetBin(buffersinkCtx, "channel_layouts", + encCtx.GetChannelLayoutAddr(), unsafe.Sizeof(encCtx.GetChannelLayout()), ffmpeg.AV_OPT_SEARCH_CHILDREN); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot set output channel layout\n") + goto end + } + + if ret = ffmpeg.AvOptSetBin(buffersinkCtx, "sample_rates", + encCtx.GetSampleRateAddr(), unsafe.Sizeof(encCtx.GetSampleRate()), ffmpeg.AV_OPT_SEARCH_CHILDREN); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot set output sample rate\n") + goto end + } + } else { + ret = ffmpeg.AVERROR_UNKNOWN + goto end + } + + // Endpoints for the filter graph. + outputs.SetName("in") + outputs.SetFilterCtx(buffersrcCtx) + outputs.SetPadIdx(0) + outputs.SetNext(nil) + + inputs.SetName("out") + inputs.SetFilterCtx(buffersinkCtx) + inputs.SetPadIdx(0) + inputs.SetNext(nil) + + if len(outputs.GetName()) == 0 || len(inputs.GetName()) == 0 { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto end + } + + if ret = ffmpeg.AvFilterGraphParsePtr(filterGraph, filterSpec, &inputs, &outputs, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot parse filter graph\n") + goto end + } + + if ret = ffmpeg.AvFilterGraphConfig(filterGraph, nil); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Cannot config filter graph\n") + goto end + } + + // Fill FilteringContext + fctx.buffersinkCtx = buffersinkCtx + fctx.buffersrcCtx = buffersrcCtx + fctx.filterGraph = filterGraph + +end: + ffmpeg.AvFilterInoutFree(&inputs) + ffmpeg.AvFilterInoutFree(&outputs) + + return ret +} + +func initFilters(ifmtCtx *ffmpeg.AVFormatContext, streamCtx []streamContext) (filterCtx []filteringContext, ret int32) { + var ( + filterSpec string + ) + + filterCtx = make([]filteringContext, ifmtCtx.GetNbStreams()) + + for i := 0; i < int(ifmtCtx.GetNbStreams()); i++ { + if !(ifmtCtx.GetStreams()[i].GetCodecpar().GetCodecType() == ffmpeg.AVMEDIA_TYPE_AUDIO || + ifmtCtx.GetStreams()[i].GetCodecpar().GetCodecType() == ffmpeg.AVMEDIA_TYPE_VIDEO) { + continue + } + + if ifmtCtx.GetStreams()[i].GetCodecpar().GetCodecType() == ffmpeg.AVMEDIA_TYPE_VIDEO { + // passthrough (dummy) filter for video + filterSpec = "null" + } else { + // passthrough (dummy) filter for audio + filterSpec = "anull" + } + if ret = initFilter(&filterCtx[i], streamCtx[i].decCtx, streamCtx[i].encCtx, filterSpec); ret > 0 { + return nil, ret + } + + if filterCtx[i].encPkt = ffmpeg.AvPacketAlloc(); filterCtx[i].encPkt == nil { + return nil, ffmpeg.AVERROR(syscall.ENOMEM) + } + + if filterCtx[i].filteredFrame = ffmpeg.AvFrameAlloc(); filterCtx[i].filteredFrame == nil { + return nil, ffmpeg.AVERROR(syscall.ENOMEM) + } + } + return filterCtx, 0 +} + +func encodeWriteFrame(ofmtCtx *ffmpeg.AVFormatContext, streamCtx []streamContext, filterCtx []filteringContext, + streamIndex int32, flush int32) (ret int32) { + var ( + stream = &streamCtx[streamIndex] + filter = &filterCtx[streamIndex] + filtFrame *ffmpeg.AVFrame + encPkt = filter.encPkt + ) + if flush == 0 { + filtFrame = filter.filteredFrame + } + + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_INFO, "Encoding frame\n") + // encode filtered frame + ffmpeg.AvPacketUnref(encPkt) + + if ret = ffmpeg.AvCodecSendFrame(stream.encCtx, filtFrame); ret < 0 { + return ret + } + + for ret >= 0 { + ret = ffmpeg.AvCodecReceivePacket(stream.encCtx, encPkt) + + if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF { + return 0 + } + + // prepare packet for muxing + encPkt.SetStreamIndex(streamIndex) + ffmpeg.AvPacketRescaleTs(encPkt, stream.encCtx.GetTimeBase(), + ofmtCtx.GetStreams()[streamIndex].GetTimeBase()) + + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_DEBUG, "Muxing frame\n") + // mux encoded frame + ret = ffmpeg.AvInterleavedWriteFrame(ofmtCtx, encPkt) + } + return ret +} + +func filterEncodeWriteFrame(ofmtCtx *ffmpeg.AVFormatContext, streamCtx []streamContext, filterCtx []filteringContext, + frame *ffmpeg.AVFrame, streamIndex int32) (ret int32) { + var ( + filter = &filterCtx[streamIndex] + ) + + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_INFO, "Pushing decoded frame to filters\n") + // 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 + } + + // pull filtered frames from the filtergraph + for { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_INFO, "Pulling filtered frame from filters\n") + if ret = ffmpeg.AvBuffersinkGetFrame(filter.buffersinkCtx, filter.filteredFrame); ret < 0 { + // if no more frames for output - returns AVERROR(EAGAIN) + // if flushed and no more frames for output - returns AVERROR_EOF + // rewrite retcode to 0 to show it as normal procedure completion + if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF { + ret = 0 + } + break + } + + filter.filteredFrame.SetPictType(ffmpeg.AV_PICTURE_TYPE_NONE) + ret = encodeWriteFrame(ofmtCtx, streamCtx, filterCtx, streamIndex, 0) + ffmpeg.AvFrameUnref(filter.filteredFrame) + if ret < 0 { + break + } + } + + return ret +} + +func flushEncoder(ofmtCtx *ffmpeg.AVFormatContext, streamCtx []streamContext, filterCtx []filteringContext, + streamIndex int32) (ret int32) { + if (streamCtx[streamIndex].encCtx.GetCodec().GetCapabilities() & ffmpeg.AV_CODEC_CAP_DELAY) == 0 { + return 0 + } + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_INFO, "Flushing stream #%d encoder\n", streamIndex) + return encodeWriteFrame(ofmtCtx, streamCtx, filterCtx, streamIndex, 1) +} + +func main() { + var ( + ret int32 + packet *ffmpeg.AVPacket + streamIndex int32 + ifmtCtx *ffmpeg.AVFormatContext + ofmtCtx *ffmpeg.AVFormatContext + streamCtx []streamContext + filterCtx []filteringContext + ) + + if len(os.Args) != 3 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Usage: %s \n", os.Args[0]) + os.Exit(1) + } + + if ifmtCtx, streamCtx, ret = openInputFile(os.Args[1]); ret < 0 { + goto end + } + if ofmtCtx, ret = openOutputFile(ifmtCtx, streamCtx, os.Args[2]); ret < 0 { + goto end + } + if filterCtx, ret = initFilters(ifmtCtx, streamCtx); ret < 0 { + goto end + } + if packet = ffmpeg.AvPacketAlloc(); packet == nil { + goto end + } + + // read all packets + for { + if ret = ffmpeg.AvReadFrame(ifmtCtx, packet); ret < 0 { + break + } + streamIndex = packet.GetStreamIndex() + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_DEBUG, "Demuxer gave frame of stream_index %d\n", streamIndex) + + if filterCtx[streamIndex].filterGraph != nil { + stream := &streamCtx[streamIndex] + + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_DEBUG, "Going to reencode&filter the frame\n") + + ffmpeg.AvPacketRescaleTs(packet, + ifmtCtx.GetStreams()[streamIndex].GetTimeBase(), + stream.decCtx.GetTimeBase()) + if ret = ffmpeg.AvCodecSendPacket(stream.decCtx, packet); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Decoding failed\n") + break + } + + for ret >= 0 { + ret = ffmpeg.AvCodecReceiveFrame(stream.decCtx, stream.decFrame) + if ret == ffmpeg.AVERROR_EOF || ret == ffmpeg.AVERROR(syscall.EAGAIN) { + break + } else if ret < 0 { + goto end + } + + stream.decFrame.SetPts(stream.decFrame.GetBestEffortTimestamp()) + if ret = filterEncodeWriteFrame(ofmtCtx, streamCtx, filterCtx, + stream.decFrame, streamIndex); ret < 0 { + goto end + } + } + } else { + // remux this frame without reencoding + ffmpeg.AvPacketRescaleTs(packet, + ifmtCtx.GetStreams()[streamIndex].GetTimeBase(), + ofmtCtx.GetStreams()[streamIndex].GetTimeBase()) + if ret = ffmpeg.AvInterleavedWriteFrame(ofmtCtx, packet); ret < 0 { + goto end + } + } + ffmpeg.AvPacketUnref(packet) + } + + // flush filters and encoders + for i := int32(0); i < int32(ifmtCtx.GetNbStreams()); i++ { + // flush filter + if filterCtx[i].filterGraph == nil { + continue + } + if ret = filterEncodeWriteFrame(ofmtCtx, streamCtx, filterCtx, nil, i); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Flushing filter failed\n") + goto end + } + + // flush encoder + if ret = flushEncoder(ofmtCtx, streamCtx, filterCtx, i); ret < 0 { + ffmpeg.AvLog(nil, ffmpeg.AV_LOG_ERROR, "Flushing encoder failed\n") + goto end + } + } + + ffmpeg.AvWriteTrailer(ofmtCtx) +end: + ffmpeg.AvPacketFree(&packet) + for i := 0; i < len(streamCtx); i++ { + ffmpeg.AvCodecFreeContext(&streamCtx[i].decCtx) + if ofmtCtx != nil && ofmtCtx.GetNbStreams() > uint32(i) && + ofmtCtx.GetStreams()[i] != nil && streamCtx[i].encCtx != nil { + ffmpeg.AvCodecFreeContext(&streamCtx[i].encCtx) + } + if filterCtx != nil && filterCtx[i].filterGraph != nil { + ffmpeg.AvFilterGraphFree(&filterCtx[i].filterGraph) + ffmpeg.AvPacketFree(&filterCtx[i].encPkt) + ffmpeg.AvFrameFree(&filterCtx[i].filteredFrame) + } + + ffmpeg.AvFrameFree(&streamCtx[i].decFrame) + } + ffmpeg.AvFormatCloseInput(&ifmtCtx) + if ofmtCtx != nil && (ofmtCtx.GetOformat().GetFlags()&ffmpeg.AVFMT_NOFILE) == 0 { + ffmpeg.AvIOClosep(ofmtCtx.GetPbAddr()) + } + ffmpeg.AvFormatFreeContext(ofmtCtx) + + if ret < 0 && ret != ffmpeg.AVERROR_EOF { + fmt.Fprintf(os.Stderr, "Error occurred: %s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + os.Exit(0) } diff --git a/examples/vaapi-encode/main.go b/examples/vaapi-encode/main.go index 7905807..fabd7e3 100644 --- a/examples/vaapi-encode/main.go +++ b/examples/vaapi-encode/main.go @@ -1,5 +1,205 @@ package main -func main() { +import ( + "fmt" + "os" + "strconv" + "syscall" + "unsafe" + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +func setHwFrameCtx(ctx *ffmpeg.AVCodecContext, hwDeviceCtx *ffmpeg.AVBufferRef, width, height int32) (ret int32) { + var ( + hwFramesRef *ffmpeg.AVBufferRef + framesCtx *ffmpeg.AVHWFramesContext + ) + + if hwFramesRef = ffmpeg.AvHWFrameCtxAlloc(hwDeviceCtx); hwFramesRef == nil { + fmt.Fprintf(os.Stderr, "Failed to create VAAPI frame context.\n") + return -1 + } + framesCtx = (*ffmpeg.AVHWFramesContext)(unsafe.Pointer(hwFramesRef.GetData())) + framesCtx.SetFormat(ffmpeg.AV_PIX_FMT_VAAPI) + framesCtx.SetSwFormat(ffmpeg.AV_PIX_FMT_NV12) + framesCtx.SetWidth(width) + framesCtx.SetHeight(height) + framesCtx.SetInitialPoolSize(20) + if ret = ffmpeg.AvHWFrameCtxInit(hwFramesRef); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize VAAPI frame context."+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + ffmpeg.AvBufferUnref(&hwFramesRef) + return ret + } + ctx.SetHwFramesCtx(ffmpeg.AvBufferRef(hwFramesRef)) + if ctx.GetHwFramesCtx() == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + } + + ffmpeg.AvBufferUnref(&hwFramesRef) + return ret +} + +func encodeWrite(avctx *ffmpeg.AVCodecContext, frame *ffmpeg.AVFrame, fout *os.File) (ret int32) { + var ( + encPkt *ffmpeg.AVPacket + ) + + if encPkt = ffmpeg.AvPacketAlloc(); encPkt == nil { + return ffmpeg.AVERROR(syscall.ENOMEM) + } + + if ret = ffmpeg.AvCodecSendFrame(avctx, frame); ret < 0 { + fmt.Fprintf(os.Stderr, "Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto end + } + for { + if ret = ffmpeg.AvCodecReceivePacket(avctx, encPkt); ret != 0 { + break + } + + encPkt.SetStreamIndex(0) + fout.Write(unsafe.Slice(encPkt.GetData(), encPkt.GetSize())) + } +end: + ffmpeg.AvPacketFree(&encPkt) + ret = ffmpeg.CondExpr(ret == ffmpeg.AVERROR(syscall.EAGAIN), 0, int32(-1)) + return ret +} + +func main() { + var ( + hwDeviceCtx *ffmpeg.AVBufferRef + fin, fout *os.File + swFrame, hwFrame *ffmpeg.AVFrame + avctx *ffmpeg.AVCodecContext + codec *ffmpeg.AVCodec + width, height, size int32 + encName = "h264_vaapi" + ret int32 + err error + ) + + if len(os.Args) < 5 { + fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0]) + os.Exit(1) + } + + if n, err := strconv.ParseInt(os.Args[1], 10, 32); err != nil { + width = int32(n) + } + if n, err := strconv.ParseInt(os.Args[2], 10, 32); err != nil { + height = int32(n) + } + size = width * height + + fin, err = os.OpenFile(os.Args[3], os.O_RDONLY, 0666) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not open %s\n", os.Args[3]) + os.Exit(1) + } + fout, err = os.OpenFile(os.Args[4], os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not open %s\n", os.Args[4]) + goto close + } + + if ret = ffmpeg.AvHWDeviceCtxCreate(&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)) + goto close + } + + if codec = ffmpeg.AvCodecFindEncoderByName(encName); codec == nil { + fmt.Fprintf(os.Stderr, "Could not find encoder.\n") + ret = -1 + goto close + } + + if avctx = ffmpeg.AvCodecAllocContext3(codec); avctx == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto close + } + avctx.SetWidth(width) + avctx.SetHeight(height) + avctx.SetTimeBase(ffmpeg.AvMakeQ(1, 25)) + avctx.SetFramerate(ffmpeg.AvMakeQ(25, 1)) + avctx.SetPixFmt(ffmpeg.AV_PIX_FMT_VAAPI) + + // set hw_frames_ctx for encoder's AVCodecContext + if ret = setHwFrameCtx(avctx, hwDeviceCtx, width, height); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to set hwframe context.\n") + goto close + } + + if ret = ffmpeg.AvCodecOpen2(avctx, codec, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open video encoder codec. Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto close + } + + for { + if swFrame = ffmpeg.AvFrameAlloc(); swFrame == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto close + } + // read data into software frame, and transfer them into hw frame + swFrame.SetWidth(width) + swFrame.SetHeight(height) + swFrame.SetFormat(ffmpeg.AV_PIX_FMT_NV12) + if ret = ffmpeg.AvFrameGetBuffer(swFrame, 0); ret < 0 { + goto close + } + frameData := ffmpeg.SliceSlice(&swFrame.GetData()[0], 2, size) + if _, err = fin.Read(frameData[0]); err != nil { + break + } + if _, err = fin.Read(frameData[1][0 : size/2]); err != nil { + break + } + + if hwFrame = ffmpeg.AvFrameAlloc(); hwFrame == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto close + } + if ret = ffmpeg.AvHWFrameGetBuffer(avctx.GetHwDeviceCtx(), hwFrame, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto close + } + if hwFrame.GetHwFramesCtx() == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto close + } + if ret = ffmpeg.AvHWFrameTransferData(hwFrame, swFrame, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Error while transferring frame data to surface."+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto close + } + + if ret = encodeWrite(avctx, hwFrame, fout); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to encode.\n") + goto close + } + ffmpeg.AvFrameFree(&hwFrame) + ffmpeg.AvFrameFree(&swFrame) + } + + // flush encoder + if ret = encodeWrite(avctx, nil, fout); ret == ffmpeg.AVERROR_EOF { + ret = 0 + } + +close: + if fin != nil { + fin.Close() + } + if fout != nil { + fout.Close() + } + ffmpeg.AvFrameFree(&swFrame) + ffmpeg.AvFrameFree(&hwFrame) + ffmpeg.AvCodecFreeContext(&avctx) + ffmpeg.AvBufferUnref(&hwDeviceCtx) + + os.Exit(int(ret)) } diff --git a/ffmpeg.go b/ffmpeg.go index 31e276b..4dfe851 100644 --- a/ffmpeg.go +++ b/ffmpeg.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/ffmpeg_helper.go b/ffmpeg_helper.go index ffbddd7..a4e0899 100644 --- a/ffmpeg_helper.go +++ b/ffmpeg_helper.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* @@ -113,3 +117,16 @@ func VoidPointerPointer(a CVoidPointerPointer) *unsafe.Pointer { } 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 { + return x + } + return y +} + +func PlusPlus[T Integer](x *T) T { + defer func() { *x++ }() + return *x +} diff --git a/postproc.go b/postproc.go index 2a21499..928f8ce 100644 --- a/postproc.go +++ b/postproc.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/postproc_version.go b/postproc_version.go index 9bde933..59ff816 100644 --- a/postproc_version.go +++ b/postproc_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/swresample.go b/swresample.go index a8da7c6..8677a7f 100644 --- a/swresample.go +++ b/swresample.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* @@ -144,8 +148,8 @@ func SwrInjectSilence(s *SwrContext, count int32) int32 { } // SwrGetDelay gets the delay the next input sample will experience relative to the next output sample. -func SwrGetDelay(s *SwrContext, base int64) int64 { - return (int64)(C.swr_get_delay((*C.struct_SwrContext)(s), (C.int64_t)(base))) +func SwrGetDelay[T Integer](s *SwrContext, base T) T { + return (T)(C.swr_get_delay((*C.struct_SwrContext)(s), (C.int64_t)(base))) } // SwrGetOutSamples Find an upper bound on the number of samples that the next swr_convert diff --git a/swresample_version.go b/swresample_version.go index 2c4c9f9..d6a583a 100644 --- a/swresample_version.go +++ b/swresample_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/swscale.go b/swscale.go index 05ddc05..0ca8f2e 100644 --- a/swscale.go +++ b/swscale.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /* diff --git a/swscale_version.go b/swscale_version.go index 24c1411..d1d040e 100644 --- a/swscale_version.go +++ b/swscale_version.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 QRTC. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + package ffmpeg /*