一步步将ffmpeg封装golang/cgo库

2023-05-16

欢迎访问博客原文:https://lightfish.cn/2018-12-24-ffmpeg-cgo

前言

继上一篇 ffmpeg音视频C编程入门, 使用高性能的C语言进行音视频的处理,比较执行效率比较高,但是业务需求,快捷开发需要使用更方便的语言,比如 golang,本文介绍如何将 将视频转成GIF 的C语言方法封装成 golang 方法以便调用。

认识cgo的封装技巧

最简单的 cgo 封装例子看这篇 cgo快速入门

我这里讲几个注意事项

  • CGO构建程序会自动构建当前目录下的C源文件,即是 go 会将当前目录下 *.c 文件都编译成 *.o目标文件,再链接汇编,这个特点衍生出几个注意事项:

    • go 代码以静态库或动态库方式引用 C 函数的话,需要将对应的C源文件移出 go源文件所在的目录
    • 如果想要将 C 函数编译到 go 程序,就需要将 C源文件与 go 文件放在同一目录下
  • 在C/C++混编下, go 中引用 C 函数,需要将 C 函数名置于全局,即 extern C

开始编程

第一步,处理例子中已经写好的 gen_gif 方法,修改 gen_gif.h 文件

#ifdef __cplusplus
extern "C" {
#endif

int gen_gif(const int gifSeconds, const int rotate, void* data, int data_size, void* outBuf, int outBufLen, int *outSize);

#ifdef __cplusplus
}
#endif

在处理 ffmpeg 的 av_log 日志的回调方法

/* log.c */
/* 
mpeg 的日志库用法

#include <libavutil/log.h>
// 设置日志级别
av_log_set_level(AV_LOG_DEBUG)
// 打印日志
av_log(NULL, AV_LOG_INFO,"...%s\n",op)

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libavutil/log.h>

// 定义输出日志的函数,留白给使用者实现
extern void Ffmpeglog(int , char*);

static void log_callback(void *avcl, int level, const char *fmt, va_list vl) 
{
    (void) avcl;
    char log[1024] = {0};
    // int vsnprintf(buffer,bufsize ,fmt, argptr) , va_list 是可变参数的指针,相关方法有: va_start(), type va_atg(va_list, type), va_end()
    int n = vsnprintf(log, 1024, fmt, vl);
    if (n > 0 && log[n - 1] == '\n')
        log[n - 1] = 0;
    if (strlen(log) == 0)
        return;
    Ffmpeglog(level, log);
}

void set_log_callback()
{
    // 给 av 解码器注册日志回调函数
    av_log_set_callback(log_callback);
}

第二步,编写 go 文件

package c

/*
#include <stdlib.h>
#include "gen_gif.h"
#include "log.h"
#cgo LDFLAGS: -lavcodec -lavformat -lswscale -lavutil -lavfilter -lm
*/
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

func init() {
	C.set_log_callback()
}

var logger func(s string) = nil

func SetFfmpegLogger(f func(s string)) {
	logger = f
}

//export Ffmpeglog
func Ffmpeglog(l C.int, t *C.char) {
	if l <= 32 {
		if logger == nil {
			fmt.Printf("ffmpeg log:%s\n", C.GoString(t))
		} else {
			logger(fmt.Sprintf("ffmpeg log:%s\n", C.GoString(t)))
		}
	}
}

func GenGif(second, rotate int, input []byte) (err error, output []byte) {
	buf := make([]byte, 1<<20)
	var outsz C.int
	ret := C.gen_gif(C.int(second), C.int(rotate), unsafe.Pointer(&input[0]), C.int(len(input)), unsafe.Pointer(&buf[0]), C.int(len(buf)), &outsz)
	if ret != 0 {
		return errors.New(fmt.Sprintf("error, ret=%v", ret)), nil
	}
	output = make([]byte, outsz)
	copy(output, buf[:outsz])
	return nil, output
}

  • 使用动态链接的方式,调用 ffmpeg 的 libav* 的函数库,设置 #cgo LDFLAGS 的动态链接选项
  • 实现 log.c 中提供使用者实现的函数,使用 go 语言的方法打印日志
  • 处理调用 C 函数传参的指针、变量,通过 import "C" 提供的方法转化变量

测试代码

将上文编写好的代码,提交到代码仓库,或者在本地的 GOPATH 中建好相应的目录,如笔者的 github.com/lightfish-zhang/mpegUtil/c 路径。

接下来,可以在业务需要的地方使用 GenGif() 了,让我们来测试一下

package main

import (
	"fmt"
	"io/ioutil"
	"os"

	mpegUtil "github.com/lightfish-zhang/mpegUtil/c"
)

func main() {
	if len(os.Args) < 3 {
		fmt.Printf("Usage: %s <input file> <output file>\n", os.Args[0])
		os.Exit(1)
	}
	inFile, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Printf("open file fail, path=%v, err=%v", os.Args[1], err)
		os.Exit(1)
	}
	outFile, err := os.Create(os.Args[2])
	if err != nil {
		fmt.Printf("create file fail, path=%v, err=%v", os.Args[2], err)
		os.Exit(1)
	}
	input, err := ioutil.ReadAll(inFile)
	if err != nil {
		fmt.Printf("read file fail, err=%v", err)
		os.Exit(1)
	}

	err, output := mpegUtil.GenGif(5, 90, input)
	if err != nil {
		fmt.Printf("generate gif fail, err=%v", err)
		os.Exit(1)
	}
	_, err = outFile.Write(output)
	if err != nil {
		fmt.Printf("write file fail, err=%v", err)
		os.Exit(1)
	}
}

我在本地执行,对某一个 mp4 文件进行转码 GIF,如

./genGif test.mp4 test.gif

运行成功,在我的电脑上可以看到完美的 GIF 图片!

动态链接或者静态链接

golang 语言的优势是打包出一个静态的执行文件,可以在相同平台下运行。但是,我们例子是动态链接了 ffmpeglibav*.so,在编译或者部署时,都需要在机器上安装好 ffmpeg 库。

有没有便利的方法进行编译或部署呢?

打包动态链接库

一个使用 LD_LIBRARY_PATH 指定动态链接目录的小技巧,不过这个技巧需要 编译机器与部署机器的运行环境差不多,除了 libav*.so 可以没有预先安装在部署机器。为了保持 libc.so 的函数可用,最好机器之间的 linux 版本一样。

使用 ldd genGif 查看编译出来的执行文件,需要动态链接哪些 *.so 文件,将关键的文件拷贝出来,比如 libav* 的 so 文件是必须的,其他比如 libm.so, libz.so 视部署机器有没有而定。

部署时候,需要把以上找到的 *.so 一起拷贝到目标机器上,同时在运行 golang 程序时,设置好全局变量 LD_LIBRARY_PATH,指向 *.so 文件目录。

静态链接编译

这个方法也需要机器的 linux 最好保持一致,ffmpeg 依赖的 libc.so 的函数好像会随着 linux 版本不同而有所差异。

准备好各种依赖库的静态库,参照如下命令

  • 编译C例子
g++ -o gen_gif -I./ -I./ffmpeg/include -I/usr/local/include main.o gen_gif.a ./ffmpeg/lib/libavdevice.a ./ffmpeg/lib/libavfilter.a ./ffmpeg/lib/libavformat.a ./ffmpeg/lib/libavcodec.a  ./ffmpeg/lib/libavutil.a  ./ffmpeg/lib/libswreample.a  ./ffmpeg/lib/libswcale.a  ./lib/liblzma.a ./lib/libm.a ./lib/libz.a ./lib/libbz2.a -lpthread
  • 编译 golang 例子,修改 #cgo 命令
/*
#cgo CFLAGS: -I./ffmpeg/include -I/usr/local/include
#cgo LDFLAGS: -L./lib -L./ffmpeg/lib
*/

Reference

cgo快速入门

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一步步将ffmpeg封装golang/cgo库 的相关文章

  • 重新采样 H264 视频以降低帧速率,同时保持高图像质量

    以下是感兴趣的视频的 mplayer 输出 br carina tmp mplayer foo mov mplayer Symbol ff codec bmp tags has different size in shared object
  • 如何使用android ndk r9b为Android编译FFMPEG

    我想设计一个Android应用程序 可以通过FFMPEG命令播放和编辑视频 但我不知道如何在Android上使用FFMPEG 我尝试过从Google搜索到的许多方法 但它们太旧了 无法实现 现在 FFMPEG的最新版本是2 1 1 Andr
  • 如何从 Linux 命令行获取视频文件的分辨率(宽度和高度)?

    我一直在挖掘 mplayer mencoder 和 ffmpeg 文档 但我似乎无法想出anything 我对输出格式不是特别挑剔 因为我可以使用正则表达式将其拉出来 我只是似乎无法首先获取数据 Use ffprobe https ffmp
  • ffmpeg AVFrame 到 opencv Mat 转换

    我目前正在开发一个使用 ffmpeg 解码接收到的帧的项目 解码后 我想将 AVFrame 转换为 opencv Mat 帧 以便我可以在 imShow 函数上播放它 我拥有的是字节流 我将其读入缓冲区 解码为 AVFrame f fope
  • FFmpeg - 来自 NodeJS 的 RTMP 流,流比实时更快

    我的目标是在 Node 中渲染画布 并将该画布流式传输到 RTMP 服务器 最终是 Twitch 但现在我正在在本地 RTMP 服务器上测试 流式传输到 RTMP 的标准方式似乎是ffmpeg 所以我使用它 从 NodeJS 中作为子进程生
  • 同时从多个流中捕获、最佳方法以及如何减少 CPU 使用率

    我目前正在编写一个应用程序 该应用程序将捕获大量 RTSP 流 在我的例子中为 12 个 并将其显示在 QT 小部件上 当我超过大约 6 7 个流时 问题就会出现 CPU 使用率激增并且出现明显的卡顿 我认为它不是 QT 绘制函数的原因是因
  • ffmpeg concat:“不安全的文件名”

    尝试将一堆 mts 文件转换为一个大 mp4 文件 stephan rechenmonster mnt backupsystem archive2 Videos 20151222 PRIVATE AVCHD BDMV bin ffmpeg
  • 如何在 Go 中填写 void* C 指针?

    我正在尝试与 Go 中的一些 C 代码交互 使用 cgo 这一直相对简单 直到我遇到这种 相当常见 的情况 需要将指针传递给本身包含指向某些数据的指针的结构 我似乎无法弄清楚如何从 Go 中做到这一点 而不诉诸于将结构的创建放入 C 代码本
  • 如何使用 ffmpeg 设置默认流

    我有一些 m4v 文件 我想用 ffmpeg 添加字幕 我知道我需要映射流以将它们放入输出文件中 但如何确保此字幕流将是默认流 字幕是 srt 人们似乎说它们与 mp4 容器不兼容 我需要先将字幕转换为什么 另外 各种流的顺序重要吗 视频流
  • Android 中的 FFMpeg jni?

    我已经构建了 Bambuser http bambuser com opensource 提供的 FFMPEG 可执行文件和库 所以我设法构建了 Android 可执行文件和库 如何在 Eclipse 项目中链接这些库并从 Java 调用
  • 如何在 RTMP 流中嵌入 pic_timing SEI 挂钟时间码?

    我需要将我的桌面流式传输到 AWS MediaLive 服务 并且根据要求 我必须在流中包含挂钟时间码 AWS 支持人员善意地通知我 对于 h 264 编码流 我需要提供时间码作为 pic timing SEI 消息 我在 Windows
  • 连接 2 个迷你音频文件并循环播放并添加背景音乐

    我需要用循环连接 2 个小音频文件并在单个命令中添加背景音乐 我能够将两个音频文件与背景音乐连接起来 我上面给出的代码正在运行 ffmpeg i 1 mp3 i 2 mp3 i background mp3 filter complex 0
  • ffmpeg 配置复杂过滤器时出错

    ffmpeg 命令存在一些问题 也许有人可以指出我正确的方向 我使用此链接来构建我的命令 那么问题 https stackoverflow com questions 7333232 how to concatenate two mp4 f
  • id3 图像编辑后播放 mp3 时遇到问题

    由于硬件限制 我们生产的软件试图确保导入到其库中的任何音频文件 准备复制到硬件上 都是可接受的比特率 最近 我们开始使用 FFmpeg 将许多不同的音频类型转换为 mp3 以便在我们的硬件上导入和使用它们 虽然转换工作正常并且 mp3 文件
  • ffmpeg计算视频比特率

    我想知道如何计算该视频的比特率 http jell yfish us media jellyfish 30 mbps hd hevc mkv http jell yfish us media jellyfish 30 mbps hd hev
  • 如何将AVFrame转换为glTexImage2D使用的纹理?

    如您所知 AVFrame 有 2 个属性 pFrame gt data pFrame gt linesize 当我从视频 sdcard test mp4 android平台 读取帧后 并将其转换为RGB AVFrame副 img conve
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • ffmpeg 命令行,用于使用 Windows 7 从 Decklink 卡捕获(和录制)720p 音频和视频

    我正在尝试使用 Windows 7 720p 从 blackmagic Decklink 捕获卡捕获音频和视频 但我似乎无法正确获取 ffmpeg 命令行设置 ffmpeg list devices true f dshow i 虚拟 ds
  • 如何创建媒体文件的稳定校验和?

    如何仅创建媒体数据的校验和而不包含元数据以获得媒体文件的稳定标识 最好是使用支持多种格式的库的跨平台方法 例如vlc ffmpeg 或 mplayer 媒体文件应该是常见格式的音频和视频 图像也很好 好吧 可能已经晚了 11 年才得到答案
  • ffprobe show_frames 用于多个视频

    有什么方法可以在一个文件中同时查看多个视频的帧吗 我知道如何在一个视频中做到这一点 ffprobe show frameshttp myvirtualdirectory myvideo mp4 http myvirtualdirectory

随机推荐

  • HEX2DEC存储过程实现

    数据库当前有十进制转换为十六进制的函数hex 函数 xff0c 却没有十六进制转换为十进制的函数 xff0c 只能自己定义一个hex2dec xff0c 存储过程如下 xff1a span class token keyword drop
  • SQLite数据类型引起的问题——全数字字符串使用varchar出现错误

    问题 xff1a 项目中需要把某些数据保存到Android的数据库中 xff0c 因为保存的字符串全部为数字形式 xff0c SQLite把部分字符串自动转化为了科学技术法导致数据显示异常 xff0c 同时还把一些开头为0的字符串自动去掉了
  • IOS 自定义UIAlertController

    自定义UIAlertController xff1a 首先展示效果图 1 创建一个新的类来管理弹出的视图 继承于UIView 2 传建一个xib文件来自定义弹出视图 xff08 注意创建过后一定要将xib的class关联 xff09 3 在
  • python把txt文件里重复数据去重代码

    有时候会发现txt文件里有很多重复数据 xff0c 这里自写了一个去重的python程序 xff0c 供学习使用 xff01 def quchong print 39 39 50 print 39 导入txt文件中 39 num 61 0
  • ERROR 1064 (42000): You have an error in your SQL

    对于新手来说 xff0c MySQL数据库 xff0c 在命令行使用sql语句进行建库 xff0c 查库 xff0c 建表 xff0c 查表 时 xff0c MySQL 报错 xff1a ERROR 1064 42000 You have
  • 【图神经网络】GNNExplainer代码解读及其PyG实现

    GNNExplainer代码解读及其PyG实现 使用GNNExplainerGNNExplainer源码速读前向传播损失函数 基于GNNExplainer图分类解释的PyG代码示例参考资料 接上一篇博客图神经网络的可解释性方法及GNNexp
  • python之自动发送微信消息

    这篇文章主要是总结最近写自动发送微信消息的python代码时所接触的两个库 pyautogui和pyperclip的用法 在网上找了很多能实现发送微信消息的方法 xff0c 其中有使用itchat和wxpy库来实现的 xff0c 尝试过后发
  • CSU1646: HearthStone(DP)

    Description Henry十分钟爱炉石传说 Heart Stone 这款有趣的桌面卡牌游戏 我们简化的游戏规则如下 xff1a 游戏由两人对战 xff0c 出牌并尽量对对方造成最大的伤害 xff0c 一共进行 r轮 前10轮 xff
  • System.DllNotFoundException:“无法加载 DLL“XXX.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。”

    System DllNotFoundException 无法加载 DLL XXX dll 找不到指定的模块 异常来自 HRESULT 0x8007007E 一般这种情况需要按是否安装完整了vc的运行时 xff0c 可以尝试安装 VC运行库合
  • Arch-004ArchLinux搜狗输入法安装

    搜狗输入法 1 sudo pacman Rsn fcitx im fcitx configtool 2 sudo pacman S fcitx lilydjwg git fcitx sogoupinyin 3 sudo pacman S f
  • AArch64中va_list/va_start/va_arg/...的实现

    版权声明 xff1a 本文为笔者本人 ashimida 64 的原创文章 xff0c 遵循CC 4 0 BY SA版权协议 xff0c 转载请附上原文出处链接及本声明 原文链接 xff1a https blog csdn net lidan
  • 51单片机外部中断示例

    void Usart INT0 init TMOD 61 0X21 TH1 61 0XFD TL1 61 0XFD SM0 61 0 SM1 61 1 REN 61 1 TR1 61 1 ES 61 1 串口中断影响外部中断0 这句话会让程
  • Hive深入浅出UDTF

    前面两篇文章我们分析了UDF和UDAF的原理以及实现思路 xff0c 这一节我们介绍另外一种UDF UDTF User Defined Table Generating Functions xff0c 是用来解决输入一行输出多行的需求的 x
  • STM32学习(3)-GPIO相关寄存器,引脚复用和重映射,点亮LED小灯(库函数、寄存器)

    参考STM32中文参考手册 一 GPIO相关配置寄存器 每组 如GPIOA GPIOB GPIO端口的寄存器包括 1 1 端口配置寄存器 由于STM32是32位的寄存器 xff0c 一个寄存器只有32位 xff0c 但是一个I O口需要4个
  • Template parse errors: Can't bind to 'ngStyle' since it isn't a known property of 'div'. ("

    1 背景 开发个公共组件 xff0c 在组件中使用指令一直报错 template 96 lt div class 61 34 content text bgp 34 gt lt div class 61 34 content text 34
  • linux mount 远程服务器共享目录

    NFS是文件系统 在网络存储方面我们应该有所了解 那么针对NFS服务器的安装和设置我们来详细介绍一下 首先让我们看一下NFS服务器的安装步骤 一 NFS服务器的安装 检查linux系统中是否安装了nfs utils和portmap两个软件包
  • 【每日一题】969. 煎饼排序

    969 煎饼排序 题目描述解决方案 xff1a 类选择排序法代码 xff1a Python 题目来源 xff1a Leetcode 原文链接 xff1a https mp weixin qq com s jboDC0R oYAy ssCXp
  • 让Num Lock默认开启

    让Num Lock默认开启 2008年11月18日 星期二 10 46 A M 1 对于2000或者XP操作系统 xff0c 登陆前NUM LOCK默认为关闭 xff0c 此为正常现象 xff0c 若用户需要此功能 xff0c 则需更改注册
  • Windows下使用Sublime Text配置C++编译环境

    1 打开Sublime xff0c 选择Tools gt Build System gt New Build System 2 将以下代码复制粘贴到新文件中去 34 span class hljs attribute path span 3
  • 一步步将ffmpeg封装golang/cgo库

    欢迎访问博客原文 xff1a https lightfish cn 2018 12 24 ffmpeg cgo 前言 继上一篇 ffmpeg音视频C编程入门 使用高性能的C语言进行音视频的处理 xff0c 比较执行效率比较高 xff0c 但