2023-04-30:用go语言重写ffmpeg的resampling_audio.c示例,它实现了音频重采样的功能。

发布时间 2023-04-30 22:54:01作者: 福大大架构师每日一题

2023-04-30:用go语言重写ffmpeg的resampling_audio.c示例,它实现了音频重采样的功能。

答案2023-04-30:

resampling_audio.c 是 FFmpeg 中的一个源文件,其主要功能是实现音频重采样。

音频重采样是指将一段音频数据从一个采样率、声道数或样本格式转换为另一种采样率、声道数或样本格式。在实际应用中,不同的设备和系统可能需要不同的音频格式,因此进行音频重采样是非常常见的操作。

resampling_audio.c 中实现了多种音频重采样算法,包括最近邻插值法、线性插值法、升采样过滤器、降采样过滤器等等。这些算法可以针对不同的输入和输出音频格式进行选择,以达到最佳效果。

使用 resampling_audio.c 可以方便地完成音频重采样操作,并在保证音质的同时提高处理效率。因此,它是 FFmpeg 中非常重要的一个模块。

代码见github/moonfdd/ffmpeg-go库。

这段代码是一个使用 FFmpeg 中的 libswresample 库进行音频重采样的示例程序。大体过程如下:

--1. 初始化输入和输出音频参数,包括声道数、采样率、样本格式等。

--3. 创建 libswresample 的上下文(SwrContext)。

--5. 通过 AvOptSetXXX 函数设置输入输出参数。

--7. 调用 SwrInit 函数初始化 resampler 上下文。

--9. 申请输入和输出音频数据缓冲区。

--11. 循环读取输入音频数据,重采样并保存为输出音频数据。每次循环中:

----a. 填充源音频数据缓冲区(即生成或从文件中读取音频数据)。

----b. 计算重采样后的目标音频数据大小。

----c. 申请足够的输出音频数据缓冲区空间。

----d. 调用 SwrConvert 函数将源音频数据转换为目标音频数据。

----e. 将重采样后的目标音频数据写入输出文件。

--13. 释放资源并退出程序。

需要注意的是,在实际使用中需要根据具体情况调整输入输出音频参数以及重采样算法等设置。

命令如下:

go run ./examples/internalexamples/resampling_audio/main.go ./out/res.aac

./lib/ffplay -f s16le -channel_layout 7 -channels 3 -ar 44100 ./out/res.aac

golang代码如下:

package main

import (
	"fmt"
	"math"
	"os"
	"unsafe"

	"github.com/moonfdd/ffmpeg-go/ffcommon"
	"github.com/moonfdd/ffmpeg-go/libavutil"
	"github.com/moonfdd/ffmpeg-go/libswresample"
)

func main0() (ret ffcommon.FInt) {
	var src_ch_layout ffcommon.FInt64T = libavutil.AV_CH_LAYOUT_STEREO
	var dst_ch_layout ffcommon.FInt64T = libavutil.AV_CH_LAYOUT_SURROUND
	var src_rate ffcommon.FInt = 48000
	var dst_rate ffcommon.FInt = 44100
	var src_data, dst_data **ffcommon.FUint8T
	var src_nb_channels, dst_nb_channels ffcommon.FInt
	var src_linesize, dst_linesize ffcommon.FInt
	var src_nb_samples ffcommon.FInt = 1024
	var dst_nb_samples ffcommon.FInt
	var max_dst_nb_samples ffcommon.FInt
	var src_sample_fmt libavutil.AVSampleFormat = libavutil.AV_SAMPLE_FMT_DBL
	var dst_sample_fmt libavutil.AVSampleFormat = libavutil.AV_SAMPLE_FMT_S16
	var dst_filename string
	var dst_file *os.File
	var dst_bufsize ffcommon.FInt
	var fmt0 string
	var swr_ctx *libswresample.SwrContext
	var t ffcommon.FDouble

	if len(os.Args) != 2 {
		fmt.Printf("Usage: %s output_file\nAPI example program to show how to resample an audio stream with libswresample.\nThis 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)
	}
	dst_filename = os.Args[1]
	dst_file, _ = os.Create(dst_filename)
	if dst_file == nil {
		fmt.Printf("Could not open destination file %s\n", dst_filename)
		os.Exit(1)
	}

	/* create resampler context */
	swr_ctx = libswresample.SwrAlloc()
	if swr_ctx == nil {
		fmt.Printf("Could not allocate resampler context\n")
		ret = -libavutil.ENOMEM
		goto end
	}

	/* set options */
	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(swr_ctx)), "in_channel_layout", src_ch_layout, 0)
	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(swr_ctx)), "in_sample_rate", int64(src_rate), 0)
	libavutil.AvOptSetSampleFmt(uintptr(unsafe.Pointer(swr_ctx)), "in_sample_fmt", src_sample_fmt, 0)

	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(swr_ctx)), "out_channel_layout", dst_ch_layout, 0)
	libavutil.AvOptSetInt(uintptr(unsafe.Pointer(swr_ctx)), "out_sample_rate", int64(src_rate), 0)
	libavutil.AvOptSetSampleFmt(uintptr(unsafe.Pointer(swr_ctx)), "out_sample_fmt", dst_sample_fmt, 0)

	/* initialize the resampling context */
	ret = swr_ctx.SwrInit()
	if ret < 0 {
		fmt.Printf("Failed to initialize the resampling context\n")
		goto end
	}

	/* allocate source and destination samples buffers */

	src_nb_channels = libavutil.AvGetChannelLayoutNbChannels(uint64(src_ch_layout))
	ret = libavutil.AvSamplesAllocArrayAndSamples(&src_data, &src_linesize, src_nb_channels,
		src_nb_samples, src_sample_fmt, 0)
	if ret < 0 {
		fmt.Printf("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 */
	dst_nb_samples = int32(libavutil.AvRescaleRnd(int64(src_nb_samples), int64(dst_rate), int64(src_rate), libavutil.AV_ROUND_UP))
	max_dst_nb_samples = dst_nb_samples

	/* buffer is going to be directly written to a rawaudio file, no alignment */
	dst_nb_channels = libavutil.AvGetChannelLayoutNbChannels(uint64(dst_ch_layout))
	ret = libavutil.AvSamplesAllocArrayAndSamples(&dst_data, &dst_linesize, dst_nb_channels,
		dst_nb_samples, dst_sample_fmt, 0)
	if ret < 0 {
		fmt.Printf("Could not allocate destination samples\n")
		goto end
	}

	t = 0
	for {
		/* generate synthetic audio */
		fill_samples((*float64)(unsafe.Pointer(*src_data)), src_nb_samples, src_nb_channels, src_rate, &t)

		/* compute destination number of samples */
		dst_nb_samples = int32(libavutil.AvRescaleRnd(swr_ctx.SwrGetDelay(int64(src_rate))+
			int64(src_nb_samples), int64(dst_rate), int64(src_rate), libavutil.AV_ROUND_UP))
		if dst_nb_samples > max_dst_nb_samples {
			libavutil.AvFreep(uintptr(unsafe.Pointer(dst_data)))
			ret = libavutil.AvSamplesAlloc(dst_data, &dst_linesize, dst_nb_channels,
				dst_nb_samples, dst_sample_fmt, 1)
			if ret < 0 {
				break
			}
			max_dst_nb_samples = dst_nb_samples
		}

		/* convert to destination format */
		ret = swr_ctx.SwrConvert(dst_data, dst_nb_samples, src_data, src_nb_samples)
		if ret < 0 {
			fmt.Printf("Error while converting\n")
			goto end
		}
		dst_bufsize = libavutil.AvSamplesGetBufferSize(&dst_linesize, dst_nb_channels,
			ret, dst_sample_fmt, 1)
		if dst_bufsize < 0 {
			fmt.Printf("Could not get sample buffer size\n")
			goto end
		}
		fmt.Printf("t:%f in:%d out:%d\n", t, src_nb_samples, ret)
		dst_file.Write(ffcommon.ByteSliceFromByteP(*dst_data, int(dst_bufsize)))
		if t < 10 {

		} else {
			break
		}
	}

	ret = get_format_from_sample_fmt(&fmt0, dst_sample_fmt)
	if ret < 0 {
		goto end
	}
	fmt.Printf("Resampling succeeded. Play the output file with the command:\nffplay -f %s -channel_layout %d -channels %d -ar %d %s\n",
		fmt0, dst_ch_layout, dst_nb_channels, dst_rate, dst_filename)

end:
	dst_file.Close()

	if src_data != nil {
		libavutil.AvFreep(uintptr(unsafe.Pointer(src_data)))
	}
	libavutil.AvFreep(uintptr(unsafe.Pointer(&src_data)))

	if dst_data != nil {
		libavutil.AvFreep(uintptr(unsafe.Pointer(dst_data)))
	}
	libavutil.AvFreep(uintptr(unsafe.Pointer(&dst_data)))

	libswresample.SwrFree(&swr_ctx)
	if ret < 0 {
		return 1
	} else {
		return 0
	}
}

func get_format_from_sample_fmt(fmt0 *string, sample_fmt libavutil.AVSampleFormat) (ret ffcommon.FInt) {
	switch sample_fmt {
	case libavutil.AV_SAMPLE_FMT_U8:
		*fmt0 = "u8"
	case libavutil.AV_SAMPLE_FMT_S16:
		*fmt0 = "s16le"
	case libavutil.AV_SAMPLE_FMT_S32:
		*fmt0 = "s32le"
	case libavutil.AV_SAMPLE_FMT_FLT:
		*fmt0 = "f32le"
	case libavutil.AV_SAMPLE_FMT_DBL:
		*fmt0 = "f64le"
	default:
		fmt.Printf("sample format %s is not supported as output format\n",
			libavutil.AvGetSampleFmtName(sample_fmt))
		ret = -1
	}
	return
}

/**
* Fill dst buffer with nb_samples, generated starting from t.
 */
func fill_samples(dst *ffcommon.FDouble, nb_samples, nb_channels, sample_rate ffcommon.FInt, t *ffcommon.FDouble) {
	var i, j ffcommon.FInt
	tincr := 1.0 / float64(sample_rate)
	dstp := dst
	c := 2 * libavutil.M_PI * 440.0

	/* generate sin tone with 440Hz frequency and duplicated channels */
	for i = 0; i < nb_samples; i++ {
		*dstp = math.Sin(c * *t)
		for j = 1; j < nb_channels; j++ {
			*(*float64)(unsafe.Pointer(uintptr(unsafe.Pointer(dstp)) + uintptr(8*j))) = *dstp
		}
		dstp = (*ffcommon.FDouble)(unsafe.Pointer(uintptr(unsafe.Pointer(dstp)) + uintptr(8*nb_channels)))
		*t += tincr
	}
}

func main() {

	os.Setenv("Path", os.Getenv("Path")+";./lib")
	ffcommon.SetAvutilPath("./lib/avutil-56.dll")
	ffcommon.SetAvcodecPath("./lib/avcodec-58.dll")
	ffcommon.SetAvdevicePath("./lib/avdevice-58.dll")
	ffcommon.SetAvfilterPath("./lib/avfilter-56.dll")
	ffcommon.SetAvformatPath("./lib/avformat-58.dll")
	ffcommon.SetAvpostprocPath("./lib/postproc-55.dll")
	ffcommon.SetAvswresamplePath("./lib/swresample-3.dll")
	ffcommon.SetAvswscalePath("./lib/swscale-5.dll")

	genDir := "./out"
	_, err := os.Stat(genDir)
	if err != nil {
		if os.IsNotExist(err) {
			os.Mkdir(genDir, 0777) //  Everyone can read write and execute
		}
	}

	main0()
}

在这里插入图片描述