前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2023-03-12:mp3音频解码为pcm,代码用go语言编写,调用moonfdd/ffmpeg-go库。

2023-03-12:mp3音频解码为pcm,代码用go语言编写,调用moonfdd/ffmpeg-go库。

作者头像
福大大架构师每日一题
发布2023-06-08 14:54:24
3040
发布2023-06-08 14:54:24
举报

2023-03-12:mp3音频解码为pcm,代码用go语言编写,调用moonfdd/ffmpeg-go库。

答案2023-03-12:

用github/moonfdd/ffmpeg-go库。

命令如下:

代码语言:javascript
复制
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编写。如下:

代码语言:javascript
复制
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,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 福大大架构师每日一题 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档