golang FFmpeg全面绑定与多媒体处理插件库goav的使用

Golang FFmpeg全面绑定与多媒体处理插件库goav的使用

废弃通知

该仓库已不再维护。

goav简介

goav是一个针对FFmpeg的Golang绑定库,提供了对ffmpeg视频/音频处理库的全面绑定。

基本用法

import "github.com/giorgisio/goav/avformat"

func main() {

	filename := "sample.mp4"

	// 注册所有格式和编解码器
	avformat.AvRegisterAll()

	ctx := avformat.AvformatAllocContext()

	// 打开视频文件
	if avformat.AvformatOpenInput(&ctx, filename, nil, nil) != 0 {
		log.Println("Error: Couldn't open file.")
		return
	}

	// 获取流信息
	if ctx.AvformatFindStreamInfo(nil) < 0 {
		log.Println("Error: Couldn't find stream information.")

		// 关闭输入文件并释放上下文
		ctx.AvformatCloseInput()
		return
	}

	//...
}

支持的库

  • avcodec 对应FFmpeg库: libavcodec [提供更广泛的编解码器实现]
  • avformat 对应FFmpeg库: libavformat [实现流协议、容器格式和基本I/O访问]
  • avutil 对应FFmpeg库: libavutil [包含哈希器、解压缩器和各种实用函数]
  • avfilter 对应FFmpeg库: libavfilter [提供通过过滤器链改变解码后的音频和视频的方法]
  • avdevice 对应FFmpeg库: libavdevice [提供访问捕获和播放设备的抽象]
  • swresample 对应FFmpeg库: libswresample [实现音频混合和重采样例程]
  • swscale 对应FFmpeg库: libswscale [实现颜色转换和缩放例程]

安装方法

首先安装FFmpeg开发库:

sudo apt-get -y install autoconf automake build-essential libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev

sudo apt install -y libavdevice-dev libavfilter-dev libswscale-dev libavcodec-dev libavformat-dev libswresample-dev libavutil-dev

sudo apt-get install yasm

export FFMPEG_ROOT=$HOME/ffmpeg
export CGO_LDFLAGS="-L$FFMPEG_ROOT/lib/ -lavcodec -lavformat -lavutil -lswscale -lswresample -lavdevice -lavfilter"
export CGO_CFLAGS="-I$FFMPEG_ROOT/include"
export LD_LIBRARY_PATH=$HOME/ffmpeg/lib

然后安装goav库:

go get github.com/giorgisio/goav

更多示例

更多编码示例可在examples/目录中找到。

注意事项

  • Go中的函数名称与库中的函数名称保持一致,便于搜索
  • goav不提供任何形式的保证

贡献指南

  • 分叉此仓库并创建您自己的功能分支
  • 遵循标准的Go惯例
  • 测试您的代码
  • 创建拉取请求

待办事项

  • 错误返回处理
  • 垃圾回收
  • 更多教程
  • Go测试
  • 可能的包重构

许可证

该库使用MIT许可证


更多关于golang FFmpeg全面绑定与多媒体处理插件库goav的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang FFmpeg全面绑定与多媒体处理插件库goav的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


GoAV: Golang的FFmpeg全面绑定与多媒体处理

GoAV是一个强大的Golang绑定库,它提供了对FFmpeg多媒体处理功能的全面访问。下面我将详细介绍GoAV的使用方法,并提供实用的代码示例。

GoAV简介

GoAV(goav)是FFmpeg的Golang绑定,允许开发者在Go程序中直接调用FFmpeg的功能进行音视频处理。它比直接调用FFmpeg命令行工具更灵活高效。

安装准备

首先需要安装FFmpeg开发库:

# Ubuntu/Debian
sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev libavdevice-dev libavfilter-dev libswscale-dev libswresample-dev

# macOS
brew install ffmpeg

然后安装GoAV:

go get github.com/giorgisio/goav

基础使用示例

1. 获取视频文件信息

package main

import (
	"fmt"
	"github.com/giorgisio/goav/avformat"
)

func main() {
	// 注册所有编解码器和格式
	avformat.AvRegisterAll()

	// 打开输入文件
	ctx := avformat.AvformatAllocContext()
	if avformat.AvformatOpenInput(&ctx, "input.mp4", nil, nil) != 0 {
		fmt.Println("无法打开文件")
		return
	}
	defer ctx.AvformatCloseInput()

	// 获取流信息
	if ctx.AvformatFindStreamInfo(nil) < 0 {
		fmt.Println("无法获取流信息")
		return
	}

	// 打印文件信息
	fmt.Printf("格式: %s, 时长: %d秒\n", ctx.iformat().Name(), ctx.Duration()/avformat.AV_TIME_BASE)
	
	// 遍历所有流
	for i := 0; i < int(ctx.NbStreams()); i++ {
		stream := ctx.Streams()[i]
		codec := stream.CodecParameters()
		fmt.Printf("流 #%d: 类型 %s, 编码 %s\n", 
			i,
			avGetMediaTypeString(codec.AvCodecGetType()),
			avcodec.AvcodecGetName(codec.AvCodecGetId()))
	}
}

func avGetMediaTypeString(t avformat.AvMediaType) string {
	switch t {
	case avformat.AVMEDIA_TYPE_VIDEO:
		return "视频"
	case avformat.AVMEDIA_TYPE_AUDIO:
		return "音频"
	default:
		return "其他"
	}
}

2. 视频转码示例

package main

import (
	"fmt"
	"github.com/giorgisio/goav/avcodec"
	"github.com/giorgisio/goav/avformat"
	"github.com/giorgisio/goav/avutil"
	"github.com/giorgisio/goav/swscale"
)

func main() {
	// 初始化
	avformat.AvRegisterAll()
	avcodec.AvcodecRegisterAll()

	// 打开输入文件
	inputCtx := avformat.AvformatAllocContext()
	if avformat.AvformatOpenInput(&inputCtx, "input.mp4", nil, nil) != 0 {
		fmt.Println("无法打开输入文件")
		return
	}
	defer inputCtx.AvformatCloseInput()

	// 创建输出文件
	outputCtx := avformat.AvformatAllocContext()
	if avformat.AvformatAllocOutputContext2(&outputCtx, nil, "mp4", "output.mp4") < 0 {
		fmt.Println("无法创建输出文件")
		return
	}
	defer outputCtx.AvformatFreeContext()

	// 查找视频流
	videoStreamIndex := -1
	for i := 0; i < int(inputCtx.NbStreams()); i++ {
		stream := inputCtx.Streams()[i]
		if stream.CodecParameters().AvCodecGetType() == avformat.AVMEDIA_TYPE_VIDEO {
			videoStreamIndex = i
			break
		}
	}

	if videoStreamIndex == -1 {
		fmt.Println("未找到视频流")
		return
	}

	// 配置输出流
	outStream := outputCtx.AvformatNewStream(nil)
	if outStream == nil {
		fmt.Println("无法创建输出流")
		return
	}

	// 复制编解码器参数
	if avcodec.AvcodecParametersCopy(outStream.CodecParameters(), 
		inputCtx.Streams()[videoStreamIndex].CodecParameters()) < 0 {
		fmt.Println("无法复制编解码器参数")
		return
	}

	// 打开输出文件
	if outputCtx.AvioOpen("output.mp4", avformat.AVIO_FLAG_WRITE) < 0 {
		fmt.Println("无法打开输出文件")
		return
	}

	// 写入文件头
	if outputCtx.AvformatWriteHeader(nil) < 0 {
		fmt.Println("无法写入文件头")
		return
	}

	// 转码处理
	packet := avcodec.AvPacketAlloc()
	for inputCtx.AvReadFrame(packet) >= 0 {
		if packet.StreamIndex() == videoStreamIndex {
			packet.SetStreamIndex(outStream.Index())
			outputCtx.AvInterleavedWriteFrame(packet)
		}
		avcodec.AvPacketUnref(packet)
	}

	// 写入文件尾
	outputCtx.AvWriteTrailer()
	fmt.Println("转码完成")
}

3. 视频帧处理

package main

import (
	"fmt"
	"image/jpeg"
	"os"
	"github.com/giorgisio/goav/avcodec"
	"github.com/giorgisio/goav/avformat"
	"github.com/giorgisio/goav/avutil"
	"github.com/giorgisio/goav/swscale"
)

func main() {
	avformat.AvRegisterAll()
	avcodec.AvcodecRegisterAll()

	// 打开视频文件
	ctx := avformat.AvformatAllocContext()
	if avformat.AvformatOpenInput(&ctx, "input.mp4", nil, nil) != 0 {
		fmt.Println("无法打开文件")
		return
	}
	defer ctx.AvformatCloseInput()

	// 查找视频流
	videoStreamIndex := -1
	var codecCtx *avcodec.Context
	for i := 0; i < int(ctx.NbStreams()); i++ {
		stream := ctx.Streams()[i]
		if stream.CodecParameters().AvCodecGetType() == avformat.AVMEDIA_TYPE_VIDEO {
			videoStreamIndex = i
			// 获取解码器
			codec := avcodec.AvcodecFindDecoder(stream.CodecParameters().AvCodecGetId())
			if codec == nil {
				fmt.Println("找不到解码器")
				return
			}
			// 创建解码器上下文
			codecCtx = avcodec.AvcodecAllocContext3(codec)
			if avcodec.AvcodecParametersToContext(codecCtx, stream.CodecParameters()) < 0 {
				fmt.Println("无法复制编解码器参数")
				return
			}
			// 打开解码器
			if avcodec.AvcodecOpen2(codecCtx, codec, nil) < 0 {
				fmt.Println("无法打开解码器")
				return
			}
			break
		}
	}

	if videoStreamIndex == -1 {
		fmt.Println("未找到视频流")
		return
	}

	// 准备帧和包
	frame := avutil.AvFrameAlloc()
	rgbFrame := avutil.AvFrameAlloc()
	packet := avcodec.AvPacketAlloc()

	// 创建图像转换上下文
	swsCtx := swscale.SwsGetContext(
		codecCtx.Width(), codecCtx.Height(), codecCtx.PixFmt(),
		codecCtx.Width(), codecCtx.Height(), avutil.AV_PIX_FMT_RGB24,
		swscale.SWS_BILINEAR, nil, nil, nil)

	// 分配RGB帧缓冲区
	avutil.AvImageAlloc(
		rgbFrame.Data(), rgbFrame.Linesize(),
		codecCtx.Width(), codecCtx.Height(), avutil.AV_PIX_FMT_RGB24, 1)

	// 读取并解码帧
	frameCount := 0
	for ctx.AvReadFrame(packet) >= 0 {
		if packet.StreamIndex() == videoStreamIndex {
			// 发送包到解码器
			if avcodec.AvcodecSendPacket(codecCtx, packet) < 0 {
				continue
			}

			// 接收解码后的帧
			for avcodec.AvcodecReceiveFrame(codecCtx, frame) >= 0 {
				// 转换帧格式为RGB
				swscale.SwsScale(swsCtx, frame.Data(), frame.Linesize(),
					0, codecCtx.Height(), rgbFrame.Data(), rgbFrame.Linesize())

				// 保存为JPEG
				saveAsJPEG(rgbFrame, codecCtx.Width(), codecCtx.Height(), fmt.Sprintf("frame%d.jpg", frameCount))
				frameCount++
			}
		}
		avcodec.AvPacketUnref(packet)
	}

	fmt.Printf("提取了%d帧\n", frameCount)
}

func saveAsJPEG(frame *avutil.Frame, width, height int, filename string) {
	file, err := os.Create(filename)
	if err != nil {
		return
	}
	defer file.Close()

	img := &image.RGBA{
		Pix:    frame.Data()[0],
		Stride: frame.Linesize()[0],
		Rect:   image.Rect(0, 0, width, height),
	}

	jpeg.Encode(file, img, &jpeg.Options{Quality: 90})
}

高级功能

1. 视频滤镜处理

// 初始化滤镜图
filterGraph := avfilter.AvfilterGraphAlloc()
src := filterGraph.AvfilterGraphAllocFilter("buffer", "in")
sink := filterGraph.AvfilterGraphAllocFilter("buffersink", "out")

// 配置输入参数
args := fmt.Sprintf("video_size=%dx%d:pix_fmt=%d:time_base=%d/%d", 
	width, height, pixFmt, timeBaseNum, timeBaseDen)
filterGraph.AvfilterGraphParsePtr("scale=w=640:h=480", &inputs, &outputs, nil)

// 处理帧
avfilter.AvBuffersrcAddFrame(srcCtx, frame)
avfilter.AvBuffersinkGetFrame(sinkCtx, filteredFrame)

2. 音频处理

// 重采样
swrCtx := swresample.SwrAllocSetOpts(
	int64(outLayout), outFormat, outRate,
	int64(inLayout), inFormat, inRate,
	0, nil)
swresample.SwrInit(swrCtx)
swresample.SwrConvert(swrCtx, outData, outCount, inData, inCount)

性能优化建议

  1. 使用硬件加速解码/编码
  2. 批量处理帧减少内存分配
  3. 合理设置缓冲区大小
  4. 多线程处理不同流
  5. 复用解码器/编码器上下文

总结

GoAV提供了对FFmpeg功能的全面访问,使得在Go中进行多媒体处理变得简单高效。通过上述示例,你可以实现视频信息获取、转码、帧处理等常见功能。对于更复杂的需求,可以进一步探索FFmpeg的滤镜系统、硬件加速等高级特性。

注意:实际使用时需要根据具体需求调整代码,并添加适当的错误处理。

回到顶部