2023-03-09:用golang调用ffmpeg,将流媒体数据(以RTMP为例)保存成本地文件(以flv为例)。
2023-03-09:用golang调用ffmpeg,将流媒体数据(以RTMP为例)保存成本地文件(以flv为例)。
答案2023-03-09:
这是最简单的收流器。本文记录一个最简单的基于FFmpeg的收流器。收流器和推流器的作用正好相反:推流器用于将本地文件以流媒体的形式发送出去,而收流器用于将流媒体内容保存为本地文件。
本文记录的推流器可以将RTMP流媒体保存成为一个本地的FLV文件。由于FFmpeg本身支持很多的流媒体协议和封装格式,所以也支持其它的封装格式和流媒体协议。
使用 github.com/moonfdd/ffmpeg-go 库,收流器的代码写在了这个库里,基于雷霄骅的代码修改。
需要修改代码里的rtmp地址,不然程序会报错。
一、先启动lalserver。lal是go语言开源的流媒体服务器。
 二、执行命令:
go run ./examples/leixiaohua1020/simplest_ffmpeg_streamer/main.go
go run ./examples/leixiaohua1020/simplest_ffmpeg_receiver/main.go
./lib/ffplay ./out/receive.flv
参考了雷霄骅的基于libx265的视频编码器,代码用golang编写。代码如下:
// https://github.com/leixiaohua1020/simplest_ffmpeg_streamer/blob/master/simplest_ffmpeg_receiver/simplest_ffmpeg_receiver.cpp
package main
import (
	"fmt"
	"os"
	"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"
)
// '1': Use H.264 Bitstream Filter
const USE_H264BSF = 0
func main0() (ret ffcommon.FInt) {
	var ofmt *libavformat.AVOutputFormat
	//Input AVFormatContext and Output AVFormatContext
	var ifmt_ctx, ofmt_ctx *libavformat.AVFormatContext
	var pkt libavcodec.AVPacket
	var in_filename, out_filename string
	var i ffcommon.FInt
	var videoindex ffcommon.FInt = -1
	var frame_index ffcommon.FInt = 0
	var h264bsfc *libavcodec.AVBitStreamFilterContext
	in_filename = "rtmp://localhost/publishlive/livestream"
	//in_filename  = "rtp://233.233.233.233:6666";
	//out_filename = "receive.ts";
	//out_filename = "receive.mkv";
	out_filename = "./out/receive.flv"
	libavformat.AvRegisterAll()
	//Network
	libavformat.AvformatNetworkInit()
	//Input
	ret = libavformat.AvformatOpenInput(&ifmt_ctx, in_filename, nil, nil)
	if ret < 0 {
		fmt.Printf("Could not open input file.")
		goto end
	}
	ret = ifmt_ctx.AvformatFindStreamInfo(nil)
	if ret < 0 {
		fmt.Printf("Failed to retrieve input stream information")
		goto end
	}
	for i = 0; i < int32(ifmt_ctx.NbStreams); i++ {
		if ifmt_ctx.GetStream(uint32(i)).Codec.CodecType == libavutil.AVMEDIA_TYPE_VIDEO {
			videoindex = i
			break
		}
	}
	ifmt_ctx.AvDumpFormat(0, in_filename, 0)
	//Output
	libavformat.AvformatAllocOutputContext2(&ofmt_ctx, nil, "", out_filename) //RTMP
	if ofmt_ctx == nil {
		fmt.Printf("Could not create output context\n")
		ret = libavutil.AVERROR_UNKNOWN
		goto end
	}
	ofmt = ofmt_ctx.Oformat
	for i = 0; i < int32(ifmt_ctx.NbStreams); i++ {
		//Create output AVStream according to input AVStream
		in_stream := ifmt_ctx.GetStream(uint32(i))
		out_stream := ofmt_ctx.AvformatNewStream(in_stream.Codec.Codec)
		if out_stream == nil {
			fmt.Printf("Failed allocating output stream\n")
			ret = libavutil.AVERROR_UNKNOWN
			goto end
		}
		//Copy the settings of AVCodecContext
		ret = libavcodec.AvcodecCopyContext(out_stream.Codec, in_stream.Codec)
		if ret < 0 {
			fmt.Printf("Failed to copy context from input to output stream codec context\n")
			goto end
		}
		out_stream.Codec.CodecTag = 0
		if ofmt_ctx.Oformat.Flags&libavformat.AVFMT_GLOBALHEADER != 0 {
			out_stream.Codec.Flags |= libavcodec.AV_CODEC_FLAG_GLOBAL_HEADER
		}
	}
	//Dump Format------------------
	ofmt_ctx.AvDumpFormat(0, out_filename, 1)
	//Open output URL
	if ofmt.Flags&libavformat.AVFMT_NOFILE == 0 {
		ret = libavformat.AvioOpen(&ofmt_ctx.Pb, out_filename, libavformat.AVIO_FLAG_WRITE)
		if ret < 0 {
			fmt.Printf("Could not open output URL '%s'", out_filename)
			goto end
		}
	}
	//Write file header
	ret = ofmt_ctx.AvformatWriteHeader(nil)
	if ret < 0 {
		fmt.Printf("Error occurred when opening output URL\n")
		goto end
	}
	if USE_H264BSF != 0 {
		h264bsfc = libavcodec.AvBitstreamFilterInit("h264_mp4toannexb")
	}
	for {
		var in_stream, out_stream *libavformat.AVStream
		//Get an AVPacket
		ret = ifmt_ctx.AvReadFrame(&pkt)
		if ret < 0 {
			break
		}
		in_stream = ifmt_ctx.GetStream(pkt.StreamIndex)
		out_stream = ofmt_ctx.GetStream(pkt.StreamIndex)
		/* copy packet */
		//Convert PTS/DTS
		pkt.Pts = libavutil.AvRescaleQRnd(pkt.Pts, in_stream.TimeBase, out_stream.TimeBase, libavutil.AV_ROUND_NEAR_INF|libavutil.AV_ROUND_PASS_MINMAX)
		pkt.Dts = libavutil.AvRescaleQRnd(pkt.Dts, in_stream.TimeBase, out_stream.TimeBase, libavutil.AV_ROUND_NEAR_INF|libavutil.AV_ROUND_PASS_MINMAX)
		pkt.Duration = libavutil.AvRescaleQ(pkt.Duration, in_stream.TimeBase, out_stream.TimeBase)
		pkt.Pos = -1
		//Print to Screen
		if pkt.StreamIndex == uint32(videoindex) {
			fmt.Printf("Receive %8d video frames from input URL\n", frame_index)
			frame_index++
			if USE_H264BSF != 0 {
				h264bsfc.AvBitstreamFilterFilter(in_stream.Codec, "", &pkt.Data, (*int32)(unsafe.Pointer(&pkt.Size)), pkt.Data, int32(pkt.Size), 0)
			}
		}
		//ret = av_write_frame(ofmt_ctx, &pkt);
		ret = ofmt_ctx.AvInterleavedWriteFrame(&pkt)
		if ret < 0 {
			fmt.Printf("Error muxing packet\n")
			break
		}
		pkt.AvFreePacket()
	}
	if USE_H264BSF != 0 {
		h264bsfc.AvBitstreamFilterClose()
	}
	//Write file trailer
	ofmt_ctx.AvWriteTrailer()
end:
	libavformat.AvformatCloseInput(&ifmt_ctx)
	/* close output */
	if ofmt_ctx != nil && ofmt.Flags&libavformat.AVFMT_NOFILE == 0 {
		ofmt_ctx.Pb.AvioClose()
	}
	ofmt_ctx.AvformatFreeContext()
	if ret < 0 && ret != libavutil.AVERROR_EOF {
		fmt.Printf("Error occurred.\n")
		return -1
	}
	return 0
}
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
		}
	}
	// go func() {
	// 	time.Sleep(1000)
	// 	exec.Command("./lib/ffplay.exe", "rtmp://localhost/publishlive/livestream").Output()
	// 	if err != nil {
	// 		fmt.Println("play err = ", err)
	// 	}
	// }()
	main0()
}

 
 
2023-03-09:用golang调用ffmpeg,将流媒体数据(以RTMP为例)保存成本地文件(以flv为例)。的更多相关文章
- C# WPF 调用FFMPEG实现“SORRY 为所欲为/王境泽”表情包GIF生成软件
		C# WPF 调用FFMPEG实现“SORRY 为所欲为/王境泽”表情包GIF生成 1,调用ffmpeg将外挂字幕“嵌入”视频中,保存副本: 2,调用ffmpeg将副本视频导出为gif图片. 参考资料 ... 
- 全面总结: Golang 调用 C/C++,例子式教程
		作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ... 
- Golang 调用 C/C++,例子式教程
		大部分人学习或者使用某样东西,喜欢在直观上看到动手后的结果,才会有继续下去的兴趣. 前言: Golang 调用 C/C++ 的教程网上很多,就我目前所看到的,个人见解就是比较乱,坑也很多.希望本文能在 ... 
- golang调用c++的dll库文件
		最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ... 
- bash shell,调用ffmpeg定期截图
		#!/bin/bash #获取当前目录中所有m3u8文件,并 var=$(ls |grep '.m3u8'|cut -d '.' -f1) #死循环 = ] do #循环每个文件 for stream ... 
- 在visual studio 2010中调用ffmpeg
		转自:http://blog.sina.com.cn/s/blog_4178f4bf01018wqh.html 最近几天一直在折腾ffmpeg,在网上也查了许多资料,费了不少劲,现在在这里和大家分享一 ... 
- c++调用ffmpeg
		在自己编译好ffmpeg库后,已经迫不及待的想尝试用vs2010来调用ffmpeg,在开始调用的时候遇到了些问题,但还是解决了. 配置vs 1.右键工程-属性,在然后选择 配置属性 -> C/C ... 
- NET 2.0(C#)调用ffmpeg处理视频的方法
		另外:ffmpeg的net封装库 http://www.intuitive.sk/fflib/ NET 2.0 调用FFMPEG,并异步读取输出信息的代码...public void ConvertV ... 
- Java调用FFmpeg进行视频处理及Builder设计模式的应用
		1.FFmpeg是什么 FFmpeg(https://www.ffmpeg.org)是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.它用来干吗呢?视频采集.视频格式转化.视频 ... 
- PHP 调用ffmpeg
		PHP 调用ffmpeg linux ffmpeg安装,tar文件安装一直出错,一直无语 php-ffmpeg安装, tar文件安装也一直出错,一直无语 最后直接在系统上安装ffmpeg sudo a ... 
随机推荐
- linux 命令行下适配 nvidia 驱动 - 搬运
			linux 命令行下适配 nvidia 驱动 转自:https://www.cnblogs.com/chua-n/p/13208398.html 1. 下载相应驱动 在官网这里选择适应你 GPU 的驱 ... 
- 关于不同平台、环境下64位int型的输入输出方式(转)
			C语言 64位int 定义方式: Linux: long long Windows:__int64 C语言 64位int 输出方式: Linux: "%lld" ... 
- NTP同步时间
			什么是NTPNTP:Network Time Protocol(网络时间协议) ️ NTP 是用于同步网络中计算机时间的协议.它的用途是把计算机的时钟同步到世界协调时UTC. UTC:Universa ... 
- ECharts笔记--实现地图版的数据显示(存在bug/┭┮﹏┭┮/)
			相关描述 前几天实现了柱状图等图的数据可视化,现在就来接着实现一下"更加"形象的数据可视化吧! 具体实现如下 <%@ taglib prefix="c" ... 
- SpringBoot笔记--配置文件分类+yaml相关知识+读取配置文件内容
			配置文件 要是需要使用自己的配置替换默认配置时,需要使用后缀名为application.properties或者application.yml(application.yaml)进行配置 当然,几个文 ... 
- Trie树结构
			PrefixTree 208. 实现 Trie (前缀树) Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键.这一数据结构 ... 
- Mysql 局域网远程连接设置——Windows
			工作中,遇到mysql数据库存储于我的电脑上,而其他电脑需要共同进行读写数据(类似redis并发),所以我的电脑就必须开启mysql远程连接. 一. 授权 1. 连接数据库 mysql -uroot ... 
- ML-程序练习-Dragon
			回归问题 前期 假设已有某样例,参数为w=1.477, b=0.089,即为\(y=1.477x+0.089\) 过程分析 数据采样 首先我们需要模拟一些带有真实样本观测误差的数据(因为真实情况是真实 ... 
- 如何申请 Azure OpenAI
			一.前言 众所周知 OpenAI ChatGPT 是不对中国开放的,包括香港.就最近一个月的情况来看,陆续有 API 调用被限制.大规模账号封禁.关闭注册.无法直接使用银联支付(国内信用卡)等等,使用 ... 
- 大语言模型快速推理: 在 Habana Gaudi2 上推理 BLOOMZ
			本文将展示如何在 Habana Gaudi2 上使用 Optimum Habana.Optimum Habana 是 Gaudi2 和 Transformers 库之间的桥梁.本文设计并实现了一个大模 ... 
