2023-04-06:拥抱Golang,优化FFmpeg音频编码器,探究encode_audio.c的内部结构。
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 mainimport ("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 stringvar codec *libavcodec.AVCodecvar c *libavcodec.AVCodecContextvar frame *libavutil.AVFramevar pkt *libavcodec.AVPacketvar i, j, k ffcommon.FIntvar f *os.Filevar samples *ffcommon.FUint16Tvar t, tincr ffcommon.FFloatif 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_S16if 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.FrameSizeframe.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 = 0tincr = 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.SampleFmtsfor *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.FIntvar best_samplerate ffcommon.FIntif codec.SupportedSamplerates == nil {return 44100}p = codec.SupportedSampleratesfor *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.FUint64Tvar best_ch_layout ffcommon.FUint64Tvar best_nb_channels ffcommon.FIntif codec.ChannelLayouts == nil {return libavutil.AV_CH_LAYOUT_STEREO}p = codec.ChannelLayoutsfor *p != 0 {nb_channels := libavutil.AvGetChannelLayoutNbChannels(*p)if nb_channels > best_nb_channels {best_ch_layout = *pbest_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()
}