mirror of
https://github.com/qrtc/ffmpeg-dev-go.git
synced 2025-10-03 14:56:59 +08:00
187 lines
5.8 KiB
Go
187 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
ffmpeg "github.com/qrtc/ffmpeg-dev-go"
|
|
)
|
|
|
|
func getFormatFromSampleFmt(sampleFmt ffmpeg.AVSampleFormat) (string, int32) {
|
|
sampleFmtEntry := []struct {
|
|
sampleFmt ffmpeg.AVSampleFormat
|
|
fmtBe string
|
|
fmtLe string
|
|
}{
|
|
{ffmpeg.AV_SAMPLE_FMT_U8, "u8", "u8"},
|
|
{ffmpeg.AV_SAMPLE_FMT_S16, "s16be", "s16le"},
|
|
{ffmpeg.AV_SAMPLE_FMT_S32, "s32be", "s32le"},
|
|
{ffmpeg.AV_SAMPLE_FMT_FLT, "f32be", "f32le"},
|
|
{ffmpeg.AV_SAMPLE_FMT_DBL, "f64be", "f64le"},
|
|
}
|
|
|
|
for _, entry := range sampleFmtEntry {
|
|
if sampleFmt == entry.sampleFmt {
|
|
return ffmpeg.AV_NE(entry.fmtBe, entry.fmtLe), 0
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(os.Stderr, "sample format %s is not supported as output format\n",
|
|
ffmpeg.AvGetSampleFmtName(sampleFmt))
|
|
return ffmpeg.NIL, -1
|
|
}
|
|
|
|
// Fill dst buffer with nb_samples, generated starting from t.
|
|
func fileSamples(dst []float64, nbSamples, nbChannels, sampleRate int32, t *float64) {
|
|
var (
|
|
tincr = 1.0 / float64(sampleRate)
|
|
c = 2 * math.Pi * 440.0
|
|
)
|
|
|
|
// generate sin tone with 440Hz frequency and duplicated channels
|
|
for i := int32(0); i < nbSamples; i++ {
|
|
dst[i*nbChannels] = math.Sin(c * (*t))
|
|
for j := int32(1); j < nbChannels; j++ {
|
|
dst[i*nbChannels+j] = dst[i*nbChannels]
|
|
}
|
|
*t += tincr
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var (
|
|
srcRate, dstRate int32 = 48000, 44100
|
|
srcData, dstData **uint8
|
|
srcNbChannels, dstNbChannels int32
|
|
srcLinesize, dstLinesize int32
|
|
srcNbSamples, dstNbSamples int32 = 1024, 0
|
|
maxDstNbSamples int32
|
|
srcChLayout, dstChLayout = ffmpeg.AV_CH_LAYOUT_STEREO, ffmpeg.AV_CH_LAYOUT_SURROUND
|
|
srcSampleFmt, dstSampleFmt = ffmpeg.AV_SAMPLE_FMT_DBL, ffmpeg.AV_SAMPLE_FMT_S16
|
|
swrCtx *ffmpeg.SwrContext
|
|
ret int32
|
|
t float64
|
|
dstBufsize int32
|
|
_fmt string
|
|
)
|
|
|
|
if len(os.Args) != 2 {
|
|
fmt.Fprintf(os.Stdout, "Usage: %s output_file\n"+
|
|
"API example program to show how to resample an audio stream with libswresample.\n"+
|
|
"This program generates a series of audio frames, resamples them to a specified "+
|
|
"output format and rate and saves them to an output file named output_file.\n",
|
|
os.Args[0])
|
|
os.Exit(1)
|
|
}
|
|
dstFilename := os.Args[1]
|
|
|
|
dstFile, err := os.OpenFile(dstFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Could not open destination file %s\n", dstFilename)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// create resampler context
|
|
if swrCtx = ffmpeg.SwrAlloc(); swrCtx == nil {
|
|
fmt.Fprintf(os.Stderr, "Could not allocate resampler context\n")
|
|
ret = ffmpeg.AVERROR(syscall.ENOMEM)
|
|
goto end
|
|
}
|
|
|
|
// set options
|
|
ffmpeg.AvOptSetInt(swrCtx, "in_channel_layout", srcChLayout, 0)
|
|
ffmpeg.AvOptSetInt(swrCtx, "in_sample_rate", srcRate, 0)
|
|
ffmpeg.AvOptSetSampleFmt(swrCtx, "in_sample_fmt", srcSampleFmt, 0)
|
|
|
|
ffmpeg.AvOptSetInt(swrCtx, "out_channel_layout", dstChLayout, 0)
|
|
ffmpeg.AvOptSetInt(swrCtx, "out_sample_rate", dstRate, 0)
|
|
ffmpeg.AvOptSetSampleFmt(swrCtx, "out_sample_fmt", dstSampleFmt, 0)
|
|
|
|
// initialize the resampling context
|
|
if ret = ffmpeg.SwrInit(swrCtx); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Failed to initialize the resampling context\n")
|
|
goto end
|
|
}
|
|
|
|
// allocate source and destination samples buffers
|
|
srcNbChannels = ffmpeg.AvGetChannelLayoutNbChannels(srcChLayout)
|
|
if ret = ffmpeg.AvSamplesAllocArrayAndSamples(&srcData, &srcLinesize, srcNbChannels,
|
|
srcNbSamples, srcSampleFmt, 0); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Could not allocate source samples\n")
|
|
goto end
|
|
}
|
|
|
|
// compute the number of converted samples: buffering is avoided
|
|
// ensuring that the output buffer will contain at least all the
|
|
// converted input samples
|
|
dstNbSamples = ffmpeg.AvRescaleRnd(srcNbSamples, dstRate, srcRate, ffmpeg.AV_ROUND_UP)
|
|
maxDstNbSamples = dstNbSamples
|
|
|
|
// buffer is going to be directly written to a rawaudio file, no alignment
|
|
dstNbChannels = ffmpeg.AvGetChannelLayoutNbChannels(dstChLayout)
|
|
if ret = ffmpeg.AvSamplesAllocArrayAndSamples(&dstData, &dstLinesize, dstNbChannels,
|
|
dstNbSamples, dstSampleFmt, 0); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Could not allocate destination samples\n")
|
|
goto end
|
|
}
|
|
|
|
for ok := true; ok; ok = (t < 10) {
|
|
// generate synthetic audio
|
|
fileSamples(unsafe.Slice((*float64)(unsafe.Pointer(*srcData)), srcNbSamples*srcNbChannels),
|
|
srcNbSamples, srcNbChannels, srcRate, &t)
|
|
|
|
//compute destination number of samples
|
|
dstNbSamples = ffmpeg.AvRescaleRnd(ffmpeg.SwrGetDelay(swrCtx, srcRate)+srcNbSamples,
|
|
dstRate, srcRate, ffmpeg.AV_ROUND_UP)
|
|
if dstNbSamples > maxDstNbSamples {
|
|
ffmpeg.AvFreep(dstData)
|
|
if ret = ffmpeg.AvSamplesAlloc(dstData, &dstLinesize, dstNbChannels,
|
|
dstNbSamples, dstSampleFmt, 1); ret < 0 {
|
|
break
|
|
}
|
|
maxDstNbSamples = dstNbSamples
|
|
}
|
|
|
|
// convert to destination format
|
|
if ret = ffmpeg.SwrConvert(swrCtx, dstData, dstNbSamples, srcData, srcNbSamples); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Error while converting\n")
|
|
goto end
|
|
}
|
|
if dstBufsize = ffmpeg.AvSamplesGetBufferSize(&dstLinesize, dstNbChannels,
|
|
ret, dstSampleFmt, 1); dstBufsize < 0 {
|
|
fmt.Fprintf(os.Stderr, "Could not get sample buffer size\n")
|
|
goto end
|
|
}
|
|
fmt.Fprintf(os.Stdout, "t:%f in:%d out:%d\n", t, srcNbSamples, ret)
|
|
dstFile.Write(ffmpeg.SliceSlice(dstData, 1, dstBufsize)[0])
|
|
}
|
|
|
|
if _fmt, ret = getFormatFromSampleFmt(dstSampleFmt); ret < 0 {
|
|
goto end
|
|
}
|
|
fmt.Fprintf(os.Stderr, "Resampling succeeded. Play the output file with the command:\n"+
|
|
"ffplay -f %s -channel_layout %d -channels %d -ar %d %s\n",
|
|
_fmt, dstChLayout, dstNbChannels, dstRate, dstFilename)
|
|
|
|
end:
|
|
dstFile.Close()
|
|
|
|
if srcData != nil {
|
|
ffmpeg.AvFreep(srcData)
|
|
}
|
|
ffmpeg.AvFreep(&srcData)
|
|
|
|
if dstData != nil {
|
|
ffmpeg.AvFreep(dstData)
|
|
}
|
|
ffmpeg.AvFreep(&dstData)
|
|
|
|
ffmpeg.SwrFree(&swrCtx)
|
|
if ret < 0 {
|
|
os.Exit(1)
|
|
}
|
|
}
|