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编写。代码如下:
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"
)
const USE_H264BSF = 0
func main0() (ret ffcommon.FInt) {
var ofmt *libavformat.AVOutputFormat
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"
out_filename = "./out/receive.flv"
libavformat.AvRegisterAll()
libavformat.AvformatNetworkInit()
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)
libavformat.AvformatAllocOutputContext2(&ofmt_ctx, nil, "", out_filename)
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++ {
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
}
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
}
}
ofmt_ctx.AvDumpFormat(0, out_filename, 1)
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
}
}
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
ret = ifmt_ctx.AvReadFrame(&pkt)
if ret < 0 {
break
}
in_stream = ifmt_ctx.GetStream(pkt.StreamIndex)
out_stream = ofmt_ctx.GetStream(pkt.StreamIndex)
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
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 = ofmt_ctx.AvInterleavedWriteFrame(&pkt)
if ret < 0 {
fmt.Printf("Error muxing packet\n")
break
}
pkt.AvFreePacket()
}
if USE_H264BSF != 0 {
h264bsfc.AvBitstreamFilterClose()
}
ofmt_ctx.AvWriteTrailer()
end:
libavformat.AvformatCloseInput(&ifmt_ctx)
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)
}
}
main0()
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)