天天看點

2023-03-12:mp3音頻解碼為pcm,代碼用go語言編寫,調用moonfdd/

作者:福大大架構師每日一題

2023-03-12:mp3音頻解碼為pcm,代碼用go語言編寫,調用moonfdd/ffmpeg-go庫。

答案2023-03-12:

用github/moonfdd/ffmpeg-go庫。

指令如下:

go run ./examples/a15.audio_decode_mp32pcm/main.go           

代碼參考了[15:mp3音頻解碼為pcm](https://feater.top/ffmpeg/ffmpeg-audio-decode-mp3-to-pcm-with-cpu),代碼用golang編寫。如下:

package main

import (
	"fmt"
	"os"
	"os/exec"
	"unsafe"

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

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

	inVFileName := "./out/test.mp3"
	outFileName := "./out/test.pcm"

	// ./lib/ffmpeg -i ./resources/big_buck_bunny.mp4 -acodec libmp3lame -vn ./out/test.mp3
	//是否存在mp3檔案
	_, err = os.Stat(inVFileName)
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Println("create mp3 file")
			exec.Command("./lib/ffmpeg", "-i", "./resources/big_buck_bunny.mp4", "-acodec", "libmp3lame", "-vn", inVFileName, "-y").CombinedOutput()
		}
	}

	os.Remove(outFileName)
	f, err := os.OpenFile(outFileName, os.O_CREATE|os.O_RDWR, 0777)
	if err != nil {
		fmt.Println("open file failed,err:", err)
		return
	}

	fmtCtx := libavformat.AvformatAllocContext()
	var codecCtx *libavcodec.AVCodecContext
	pkt := libavcodec.AvPacketAlloc()
	frame := libavutil.AvFrameAlloc()

	aStreamIndex := -1

	for {
		if libavformat.AvformatOpenInput(&fmtCtx, inVFileName, nil, nil) < 0 {
			fmt.Printf("Cannot open input file.\n")
			break
		}

		if fmtCtx.AvformatFindStreamInfo(nil) < 0 {
			fmt.Printf("Cannot find stream info in input file.\n")
			break
		}

		fmtCtx.AvDumpFormat(0, inVFileName, 0)

		//查找視訊流在檔案中的位置
		for i := uint32(0); i < fmtCtx.NbStreams; i++ {
			if fmtCtx.GetStream(i).Codecpar.CodecType == libavutil.AVMEDIA_TYPE_AUDIO {
				aStreamIndex = int(i)
				break
			}
		}

		if aStreamIndex == -1 {
			fmt.Printf("Cannot find audio stream.\n")
			return
		}

		aCodecPara := fmtCtx.GetStream(uint32(aStreamIndex)).Codecpar
		codec := libavcodec.AvcodecFindDecoder(aCodecPara.CodecId)
		if codec == nil {
			fmt.Printf("Cannot find any codec for audio.\n")
			return
		}

		codecCtx = codec.AvcodecAllocContext3()

		if codecCtx.AvcodecParametersToContext(aCodecPara) < 0 {
			fmt.Printf("Cannot alloc codec context.\n")
			return
		}

		codecCtx.PktTimebase = fmtCtx.GetStream(uint32(aStreamIndex)).TimeBase

		if codecCtx.AvcodecOpen2(codec, nil) < 0 {
			fmt.Printf("Cannot open audio codec.\n")
			return
		}

		for (fmtCtx.AvReadFrame(pkt)) >= 0 {
			if pkt.StreamIndex == uint32(aStreamIndex) {
				if codecCtx.AvcodecSendPacket(pkt) >= 0 {
					for codecCtx.AvcodecReceiveFrame(frame) >= 0 {
						/*
						   Planar(平面),其資料格式排列方式為 (特别記住,該處是以點nb_samples采樣點來交錯,不是以位元組交錯):
						   LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每個LLLLLLRRRRRR為一個音頻幀)
						   而不帶P的資料格式(即交錯排列)排列方式為:
						   LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每個LR為一個音頻樣本)
						*/
						if libavutil.AvSampleFmtIsPlanar(codecCtx.SampleFmt) != 0 {
							numBytes := libavutil.AvGetBytesPerSample(codecCtx.SampleFmt)
							//pcm播放時是LRLRLR格式,是以要交錯儲存資料
							bytes := []byte{}
							for i := int32(0); i < frame.NbSamples; i++ {
								for ch := int32(0); ch < codecCtx.Channels; ch++ {
									ptr := uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(frame.Data[ch])) + uintptr(numBytes*i)))
									for k := int32(0); k < numBytes; k++ {
										bytes = append(bytes, *(*byte)(unsafe.Pointer(ptr)))
										ptr++
									}

								}
							}
							f.Write(bytes)
						}
					}
				}
			}
			pkt.AvPacketUnref()
		}

		break
	}

	libavutil.AvFrameFree(&frame)
	libavcodec.AvPacketFree(&pkt)
	codecCtx.AvcodecClose()
	libavcodec.AvcodecFreeContext(&codecCtx)
	fmtCtx.AvformatFreeContext()
	f.Close()

	fmt.Println("-----------------------------------------")
	// ./lib/ffplay -ar 22050 -ac 2 -f f32le -i ./out/test.pcm
	_, err = exec.Command("./lib/ffplay.exe", "-ar", "22050", "-ac", "2", "-f", "f32le", "-i", "./out/test.pcm").Output()
	if err != nil {
		fmt.Println("play err = ", err)
	}
}
           
2023-03-12:mp3音頻解碼為pcm,代碼用go語言編寫,調用moonfdd/

繼續閱讀