2023-04-06:拥抱Golang,优化FFmpeg音频编码器,探究encode_audio.c的内部结构。

news/2024/7/10 21:54:00 标签: ffmpeg, 音视频, golang

2023-04-06:拥抱Golang,优化FFmpeg音频编码器,探究encode_audio.c的内部结构。

答案2023-04-06:

见moonfdd/ffmpeg-go库。

这段代码是一个示例程序,用于将音频 PCM 数据编码为 MP2 格式的音频文件。下面是代码的详细步骤:

1.导入 ffmpeg-go 和 os 等 Go 库;

2.定义一些变量,包括输出文件名、音频编解码器、音频编解码上下文、音频帧、音频数据包等;

3.查找 MP2 编码器并分配音频编解码上下文;

4.配置音频编解码参数,设置音频采样率、通道数、位率等;

5.打开音频编解码器;

6.创建输出文件;

7.开始编码过程,并将编码后的音频数据写入输出文件中。

具体地,编码过程包括以下几个步骤:

1.初始化音频帧;

2.将音频 PCM 数据填充到音频帧中;

3.发送音频帧到编解码器中进行编码;

4.从编解码器中读取编码后的音频数据包;

5.将编码后的音频数据包写入输出文件中。

最后,释放内存空间并关闭文件和编码器。在该示例程序中,我们需要手动设置 FFmpeg 库的路径,以便正确加载库文件。

命令如下:

go run ./examples/internalexamples/encode_audio/main.go ./out/encode_audio.mp2

./lib/ffplay ./out/encode_audio.mp2

golang代码如下:

package main

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

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

func main0() (ret ffcommon.FInt) {
	var filename string
	var codec *libavcodec.AVCodec
	var c *libavcodec.AVCodecContext
	var frame *libavutil.AVFrame
	var pkt *libavcodec.AVPacket
	var i, j, k ffcommon.FInt
	var f *os.File
	var samples *ffcommon.FUint16T
	var t, tincr ffcommon.FFloat

	if len(os.Args) <= 1 {
		fmt.Printf("Usage: %s <output file>\n", os.Args[0])
		return 0
	}
	filename = os.Args[1]

	/* find the MP2 encoder */
	codec = libavcodec.AvcodecFindEncoder(libavcodec.AV_CODEC_ID_MP2)
	if codec == nil {
		fmt.Printf("Codec not found\n")
		os.Exit(1)
	}

	c = codec.AvcodecAllocContext3()
	if c == nil {
		fmt.Printf("Could not allocate audio codec context\n")
		os.Exit(1)
	}

	/* put sample parameters */
	c.BitRate = 64000

	/* check that the encoder supports s16 pcm input */
	c.SampleFmt = libavutil.AV_SAMPLE_FMT_S16
	if check_sample_fmt(codec, c.SampleFmt) == 0 {
		fmt.Printf("Encoder does not support sample format %s",
			libavutil.AvGetSampleFmtName(c.SampleFmt))
		os.Exit(1)
	}

	/* select other audio parameters supported by the encoder */
	c.SampleRate = select_sample_rate(codec)
	c.ChannelLayout = uint64(select_channel_layout(codec))
	c.Channels = libavutil.AvGetChannelLayoutNbChannels(c.ChannelLayout)

	/* open it */
	if c.AvcodecOpen2(codec, nil) < 0 {
		fmt.Printf("Could not open codec\n")
		os.Exit(1)
	}

	f, _ = os.Create(filename)
	if f == nil {
		fmt.Printf("Could not open %s\n", filename)
		os.Exit(1)
	}

	/* packet for holding encoded output */
	pkt = libavcodec.AvPacketAlloc()
	if pkt == nil {
		fmt.Printf("could not allocate the packet\n")
		os.Exit(1)
	}

	/* frame containing input raw audio */
	frame = libavutil.AvFrameAlloc()
	if frame == nil {
		fmt.Printf("Could not allocate audio frame\n")
		os.Exit(1)
	}

	frame.NbSamples = c.FrameSize
	frame.Format = int32(c.SampleFmt)
	frame.ChannelLayout = c.ChannelLayout

	/* allocate the data buffers */
	ret = frame.AvFrameGetBuffer(0)
	if ret < 0 {
		fmt.Printf("Could not allocate audio data buffers\n")
		os.Exit(1)
	}

	/* encode a single tone sound */
	t = 0
	tincr = float32(2 * libavutil.M_PI * 440.0 / float64(c.SampleRate))
	for i = 0; i < 200; i++ {
		/* make sure the frame is writable -- makes a copy if the encoder
		 * kept a reference internally */
		ret = frame.AvFrameMakeWritable()
		if ret < 0 {
			os.Exit(1)
		}
		samples = (*ffcommon.FUint16T)(unsafe.Pointer(frame.Data[0]))

		for j = 0; j < c.FrameSize; j++ {
			*(*ffcommon.FUint16T)(unsafe.Pointer(uintptr(unsafe.Pointer(samples)) + uintptr(2*j*2))) = ffcommon.FUint16T(math.Sin(float64(t)) * 10000)

			for k = 1; k < c.Channels; k++ {
				*(*ffcommon.FUint16T)(unsafe.Pointer(uintptr(unsafe.Pointer(samples)) + uintptr((2*j+k)*2))) = *(*ffcommon.FUint16T)(unsafe.Pointer(uintptr(unsafe.Pointer(samples)) + uintptr(2*j*2)))
			}
			t += tincr
		}
		encode(c, frame, pkt, f)
	}

	/* flush the encoder */
	encode(c, nil, pkt, f)

	f.Close()

	libavutil.AvFrameFree(&frame)
	libavcodec.AvPacketFree(&pkt)
	libavcodec.AvcodecFreeContext(&c)

	return 0
}

/* check that a given sample format is supported by the encoder */
func check_sample_fmt(codec *libavcodec.AVCodec, sample_fmt libavutil.AVSampleFormat) ffcommon.FInt {
	p := codec.SampleFmts

	for *p != libavutil.AV_SAMPLE_FMT_NONE {
		if *p == sample_fmt {
			return 1
		}
		p = (*libavutil.AVSampleFormat)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(8)))
	}
	return 0
}

/* just pick the highest supported samplerate */
func select_sample_rate(codec *libavcodec.AVCodec) ffcommon.FInt {
	var p *ffcommon.FInt
	var best_samplerate ffcommon.FInt

	if codec.SupportedSamplerates == nil {
		return 44100
	}

	p = codec.SupportedSamplerates
	for *p != 0 {
		if best_samplerate == 0 || int32(math.Abs(float64(44100-*p))) < int32(math.Abs(float64(44100-best_samplerate))) {
			best_samplerate = *p
		}
		p = (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(4)))
	}
	return best_samplerate
}

/* select layout with the highest channel count */
func select_channel_layout(codec *libavcodec.AVCodec) ffcommon.FInt {

	var p *ffcommon.FUint64T
	var best_ch_layout ffcommon.FUint64T
	var best_nb_channels ffcommon.FInt

	if codec.ChannelLayouts == nil {
		return libavutil.AV_CH_LAYOUT_STEREO
	}

	p = codec.ChannelLayouts
	for *p != 0 {
		nb_channels := libavutil.AvGetChannelLayoutNbChannels(*p)

		if nb_channels > best_nb_channels {
			best_ch_layout = *p
			best_nb_channels = nb_channels
		}
		p = (*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(8)))
	}
	return ffcommon.FInt(best_ch_layout)
}

func encode(ctx *libavcodec.AVCodecContext, frame *libavutil.AVFrame, pkt *libavcodec.AVPacket, output *os.File) {
	var ret ffcommon.FInt

	/* send the frame for encoding */
	ret = ctx.AvcodecSendFrame(frame)
	if ret < 0 {
		fmt.Printf("Error sending the frame to the encoder\n")
		os.Exit(1)
	}

	/* read all the available output packets (in general there may be any
	 * number of them */
	for ret >= 0 {
		ret = ctx.AvcodecReceivePacket(pkt)
		if ret == -libavutil.EAGAIN || ret == libavutil.AVERROR_EOF {
			return
		} else if ret < 0 {
			fmt.Printf("Error encoding audio frame\n")
			os.Exit(1)
		}

		output.Write(ffcommon.ByteSliceFromByteP(pkt.Data, int(pkt.Size)))
		pkt.AvPacketUnref()
	}
}

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()
}

在这里插入图片描述


http://www.niftyadmin.cn/n/201299.html

相关文章

基于ASP.NET的电子产品销售网站的设计与实现

在信息时代&#xff0c;日益普及的Internet不仅带给人们更多及时的资讯信息&#xff0c;而且也在生活的各个方面带给人们更多的便捷,在一定程度上改变着人类的生活方式&#xff0c;例如在线购物。人们只要通过Internet的连接&#xff0c;在个人电脑上登录各种销售网站&#xff…

接口数据使用了 RSA 加密和签名?一篇文章带你了解

接口数据使用了RSA加密和签名&#xff1f;一篇文章带你搞定&#xff01; 1、前言 很多童鞋在工作中&#xff0c;会遇到一些接口使用RSA加密和签名来处理的请求参数&#xff0c;那么遇到这个问题的时候&#xff0c;第一时间当然是找开发要加解密的方法&#xff0c;但是开发给加…

Hystrix图形化监控案例

一、相关依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency><groupId>org.springframework.boot</gr…

Java知识点学习(第9天)

什么是STW&#xff1f; STW&#xff1a;Stop The World&#xff0c;在垃圾回收算法执行的时候&#xff0c;需要将JVM内存冻结的一种状态。在STW状态下&#xff0c;Java的所有线程所有线程都是停止的&#xff08;GC线程除外&#xff09;&#xff0c;native方法&#xff08;底层…

身经百战——实战总结

面试题总结 1.token校验数据内部因素&#xff1a; JWT令牌结构&#xff1a;Header、Payload、Signature三部分组成 Jwts.builder(); 2.maven模式怎么切换环境 采用profile设定不同的开发环境 全局配置 maven路径conf/settings.xml 局部配置 pom.xml pom多环境配置中我们的…

游戏服务器开发指南(三):设计高效的线程模型

大家好&#xff01;我是长三月&#xff0c;一位在游戏行业工作多年的老程序员&#xff0c;专注于分享服务器开发相关的文章。 一周一次的系列分享又与大家见面了。这次的主题是设计高效的线程模型&#xff0c;是并发类别下的第一篇。 在游戏服务器开发中&#xff0c;一个高效…

天梯赛练习集-L1-021到L1-030–python - java

文章目录PythonL1-021 重要的话说三遍L1-022 奇偶分家L1-023 输出GPLTL1-024 后天L1-025 正整数ABL1-026 I Love GPLTL1-027 出租L1-028 判断素数L1-029 是不是太胖了L1-030 一帮一JavaL1-021 重要的话说三遍L1-022 奇偶分家L1-023 输出GPLTL1-024 后天L1-025 正整数ABL1-026 I…

Mybatis 开启控制台打印sql语句

概述 springbootmybatis整合过程中&#xff0c;开启控制台sql语句打印的多种方式&#xff1a; 方法1 在springbootmybatis整合中&#xff0c;可以将springboot的配置文件添加如下一段也可&#xff1a; logging:level:com.lucifer.springboot.cache.mapper: debugps: com.lu…