2023 您所在的位置:网站首页 ffmpeg音频转码 2023

2023

2023-04-07 01:05| 来源: 网络整理| 查看: 265

2023-03-19:使用Go语言和FFmpeg库实现pcm编码为mp3。

答案2023-03-19:

本文将介绍如何使用Go语言和FFmpeg库实现PCM音频文件编码为MP3格式。我们将使用moonfdd/ffmpeg-go库,并在Windows 10 64位操作系统下完成本次实验。

代码参考了FFmpeg —— 15.示例程序(九):音频编码器(PCM编码为MP3)和19:pcm编码为mp3。

看完整代码,这个肯定能运行通过。

1.准备工作

安装moonfdd/ffmpeg-go库,运行命令:go get -u github.com/moonfdd/ffmpeg-go

2.实现步骤 2.1.设置FFmpeg库路径

首先需要设置FFmpeg库的路径,在本例中是"./lib"目录下。通过moonfdd/ffmpeg-go库提供的函数SetXXXXPath()可以分别设置各个库的路径:

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") 2.2.准备输入PCM文件

本例中输入的PCM文件是16位采样精度、立体声(2个声道)、44100Hz采样率,文件名为"s16le.pcm",存放在"./out"目录下。如果该文件不存在,则从一个视频文件中提取音频数据并转换格式生成该PCM文件:

inFileName := "./out/s16le.pcm" _, err = os.Stat(inFileName) if err != nil { if os.IsNotExist(err) { fmt.Println("create pcm file") exec.Command("./lib/ffmpeg", "-i", "./resources/big_buck_bunny.mp4", "-f", "s16le", "-ar", "44100", "-ac", "2", "-acodec", "pcm_s16le", "-vn", inFileName, "-y").CombinedOutput() } } 2.3.打开输出MP3文件

本例中输出的MP3文件名为"out19.mp3",存放在"./out"目录下。首先需要调用libavformat.AvformatAllocOutputContext2()函数分配AVFormatContext结构体,并调用libavformat.AvioOpen()函数打开输出文件:

var pFormatCtx *libavformat.AVFormatContext libavformat.AvformatAllocOutputContext2(&pFormatCtx, nil, "", outFileName) if libavformat.AvioOpen(&pFormatCtx.Pb, outFileName, libavformat.AVIO_FLAG_READ_WRITE) < 0 { fmt.Printf("Cannot open output file.\n") return } 2.4.添加输出流

将要输出的音频流添加到输出文件中。首先需要调用libavformat.AvformatNewStream()函数创建一个新的流对象,并将该流对象的Codec属性设置为要输出的音频编解码器属性:

stream := pFormatCtx.AvformatNewStream(nil) if stream == nil { fmt.Printf("Cannot create a new stream to output file.\n") return } pCodecCtx = stream.Codec pCodecCtx.CodecType = libavutil.AVMEDIA_TYPE_AUDIO pCodecCtx.CodecId = pFormatCtx.Oformat.AudioCodec pCodecCtx.SampleFmt = libavutil.AV_SAMPLE_FMT_FLTP pCodecCtx.SampleRate = 44100 pCodecCtx.ChannelLayout = libavutil.AV_CH_LAYOUT_STEREO pCodecCtx.BitRate = 128000 pCodecCtx.Channels = libavutil.AvGetChannelLayoutNbChannels(pCodecCtx.ChannelLayout) 2.5.查找并打开编码器

根据指定的编码器ID查找对应的编码器对象,调用libavcodec.AvcodecFindEncoder()函数返回对应的AVCodec对象。然后,调用libavcodec.AvcodecOpen2()函数打开编码器并初始化编码器上下文:

pCodec := libavcodec.AvcodecFindEncoder(pCodecCtx.CodecId) if pCodec == nil { fmt.Printf("Cannot find encoder.\n") return } if pCodec.AvcodecOpen2(pCodecCtx, nil) < 0 { fmt.Printf("Cannot open encoder.\n") return } 2.6.写入文件头

调用libavformat.AvformatWriteHeader()函数写入输出文件的文件头信息:

if libavformat.AvformatWriteHeader(pFormatCtx, nil) < 0 { fmt.Printf("Error occurred while writing header.\n") return } 2.7.编码音频数据

循环读取输入PCM文件中的音频数据,将其填充到AVFrame对象中,并调用libavcodec.AvcodecSendFrame()函数发送该帧音频数据给编码器。然后循环调用libavcodec.AvcodecReceivePacket()函数接收编码器编码后的数据包,并调用libavformat.AvInterleavedWriteFrame()函数将该数据包写入输出文件中:

for { ret := inF.Read(buf) if ret == 0 { break } inBufSize := len(buf) // fill data to AVFrame structure pFrame := libavutil.AvFrameAlloc() defer libavutil.AvFrameFree(pFrame) pFrame.SetNbSamples(int32(inBufSize) / (2 * 2)) pFrame.SetFormat(pCodecCtx.SampleFmt) pFrame.SetSampleRate(pCodecCtx.SampleRate) pFrame.SetChannelLayout(pCodecCtx.ChannelLayout) for i := 0; i < int(pFrame.NbSamples()); i++ { for t := 0; t < int(pFrame.Channels()); t++ { idx := (i*int(pFrame.Channels()) + t) * 2 val := float32(int16(binary.LittleEndian.Uint16(buf[idx:idx+2]))) / (1 channels,sizeof(*convert_data)); convert_data := (**byte)(unsafe.Pointer(libavutil.AvCalloc(uint64(pCodecCtx.Channels), 8))) libavutil.AvSamplesAlloc(convert_data, nil, pCodecCtx.Channels, pCodecCtx.FrameSize, pCodecCtx.SampleFmt, 0) size := libavutil.AvSamplesGetBufferSize(nil, pCodecCtx.Channels, pCodecCtx.FrameSize, pCodecCtx.SampleFmt, 1) frameBuf := libavutil.AvMalloc(uint64(size)) libavcodec.AvcodecFillAudioFrame(pFrame, pCodecCtx.Channels, pCodecCtx.SampleFmt, (*byte)(unsafe.Pointer(frameBuf)), size, 1) //写帧头 pFormatCtx.AvformatWriteHeader(nil) inFile, err := os.Open(inFileName) if err != nil { fmt.Printf("annot open input file.\n") return } pkt.AvInitPacket() pkt.Data = nil pkt.Size = 0 for i := 0; ; i++ { //输入一帧数据的长度 length := pFrame.NbSamples * libavutil.AvGetBytesPerSample(libavutil.AV_SAMPLE_FMT_S16) * pFrame.Channels //读PCM:特意注意读取的长度,否则可能出现转码之后声音变快或者变慢 buf := make([]byte, length) n, err := inFile.Read(buf) if err != nil { fmt.Println("read end") break } if n data[0],convert_data[0],length); // memcpy(frame->data[1],convert_data[1],length); c := *(*[2]uintptr)(unsafe.Pointer(convert_data)) fd0 := uintptr(unsafe.Pointer(pFrame.Data[0])) cd0 := uintptr(unsafe.Pointer(c[0])) fd1 := uintptr(unsafe.Pointer(pFrame.Data[1])) cd1 := uintptr(unsafe.Pointer(c[1])) for j := int32(0); j < length; j++ { *(*byte)(unsafe.Pointer(fd0)) = *(*byte)(unsafe.Pointer(cd0)) *(*byte)(unsafe.Pointer(fd1)) = *(*byte)(unsafe.Pointer(cd1)) fd0++ cd0++ fd1++ cd1++ } pFrame.Pts = int64(i * 100) if pCodecCtx.AvcodecSendFrame(pFrame) < 0 { fmt.Printf("can't send frame for encoding\n") break } if pCodecCtx.AvcodecReceivePacket(&pkt) >= 0 { pkt.StreamIndex = uint32(stream.Index) fmt.Printf("write %4d frame, size = %d, length = %d\n", i, size, length) pFormatCtx.AvWriteFrame(&pkt) } pkt.AvPacketUnref() } // flush encoder if flush_encoder(pFormatCtx, 0) < 0 { fmt.Printf("flushing encoder failed\n") return } // write trailer pFormatCtx.AvWriteTrailer() inFile.Close() stream.Codec.AvcodecClose() libavutil.AvFree(uintptr(unsafe.Pointer(pFrame))) libavutil.AvFree(frameBuf) pFormatCtx.Pb.AvioClose() pFormatCtx.AvformatFreeContext() break } // codecCtx.AvcodecClose() // libavutil.AvFree(uintptr(unsafe.Pointer(frame))) // fmtCtx.Pb.AvioClose() // fmtCtx.AvformatFreeContext() fmt.Println("-----------------------------------------") // ./lib/ffplay -ar 44100 -ac 2 -f s16le -i ./out/test.pcm //_, err = exec.Command("./lib/ffplay.exe", "-ar", "44100", "-ac", "2", "-f", "s16le", "-i", "./out/test16.pcm").Output() _, err = exec.Command("./lib/ffplay.exe", outFileName).Output() if err != nil { fmt.Println("play err = ", err) } } func flush_encoder(fmt_ctx *libavformat.AVFormatContext, stream_index int) int32 { ret := int32(0) var got_frame int32 var enc_pkt libavcodec.AVPacket if fmt_ctx.GetStream(uint32(stream_index)).Codec.Codec.Capabilities&libavcodec.AV_CODEC_CAP_DELAY == 0 { return 0 } for { enc_pkt.Data = nil enc_pkt.Size = 0 enc_pkt.AvInitPacket() ret = fmt_ctx.GetStream(uint32(stream_index)).Codec.AvcodecEncodeAudio2(&enc_pkt, nil, &got_frame) //av_frame_free(NULL) if ret < 0 { break } if got_frame == 0 { ret = 0 break } fmt.Printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.Size) /* mux encoded frame */ ret = fmt_ctx.AvWriteFrame(&enc_pkt) if ret < 0 { break } } return ret } 4.运行结果

执行命令:

go run ./examples/a19.audio_encode_pcm2mp3/main.go 1679232751534.jpg


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有