2023-04-06:擁抱Golang,優(yōu)化FFmpeg音頻編碼器,探究encode_audio.c的內(nèi)部結(jié)構(gòu)。

2023-04-06:擁抱Golang,優(yōu)化FFmpeg音頻編碼器,探究encode_audio.c的內(nèi)部結(jié)構(gòu)。

答案2023-04-06:

見(jiàn)moonfdd/ffmpeg-go庫(kù)。

這段代碼是一個(gè)示例程序,用于將音頻 PCM 數(shù)據(jù)編碼為 MP2 格式的音頻文件。下面是代碼的詳細(xì)步驟:

1.導(dǎo)入 ffmpeg-go 和 os 等 Go 庫(kù);

2.定義一些變量,包括輸出文件名、音頻編解碼器、音頻編解碼上下文、音頻幀、音頻數(shù)據(jù)包等;

3.查找 MP2 編碼器并分配音頻編解碼上下文;

4.配置音頻編解碼參數(shù),設(shè)置音頻采樣率、通道數(shù)、位率等;

5.打開(kāi)音頻編解碼器;

6.創(chuàng)建輸出文件;

7.開(kāi)始編碼過(guò)程,并將編碼后的音頻數(shù)據(jù)寫(xiě)入輸出文件中。

具體地,編碼過(guò)程包括以下幾個(gè)步驟:

1.初始化音頻幀;

2.將音頻 PCM 數(shù)據(jù)填充到音頻幀中;

3.發(fā)送音頻幀到編解碼器中進(jìn)行編碼;

4.從編解碼器中讀取編碼后的音頻數(shù)據(jù)包;

5.將編碼后的音頻數(shù)據(jù)包寫(xiě)入輸出文件中。

最后,釋放內(nèi)存空間并關(guān)閉文件和編碼器。在該示例程序中,我們需要手動(dòng)設(shè)置 FFmpeg 庫(kù)的路徑,以便正確加載庫(kù)文件。

命令如下:

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

1680788733218.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容