2023-04-27:用go语言重写ffmpeg的remuxing.c示例。
2023-04-27:用go语言重写ffmpeg的remuxing.c示例。
答案2023-04-27:
ffmpeg的remuxing.c是一个用于将多媒体文件从一种容器格式转换为另一种容器格式的命令行工具。它可以将音频、视频和字幕等元素从源文件中提取出来,并按照用户指定的方式重新封装到目标文件中。在本篇文章中,我将对ffmpeg的remuxing.c进行介绍,并讨论其关键功能和技术实现。
1. remuxing.c的主要功能
remuxing.c主要有两个关键功能:提取和重封装。在提取阶段,remuxing.c会解析源文件的格式,并将其中的音频、视频和字幕等元素提取出来。在重封装阶段,remuxing.c将这些元素重新封装为另一种格式,并生成目标文件。
remuxing.c支持多种输入和输出格式,包括常见的MP4、AVI、MKV、FLV等格式。用户可以通过指定命令行参数来选择源文件和目标文件格式,并控制重封装过程中的各种选项,例如视频编码器、音频采样率、字幕格式等。
除了基本的提取和重封装功能之外,remuxing.c还支持其他高级功能,例如从流媒体服务器拉取数据、实时流处理、特定元素的删除和添加等。
2. remuxing.c技术实现
remuxing.c的技术实现主要涉及以下几个方面:
2.1 容器格式解析和重构
remuxing.c需要能够识别并解析多种容器格式,以便提取其中的音频、视频和字幕等元素。为了实现这一功能,remuxing.c使用了FFmpeg中的AVFormatContext结构体,并利用其封装和解封装函数进行文件格式的解析和重构。在提取阶段,remuxing.c通过遍历媒体文件的AVStream对象来获取其中的音频流、视频流和字幕流等元素,然后将它们存储在合适的AVCodecContext对象中。在重封装阶段,remuxing.c则使用AVOutputFormat结构体和AVStream对象来指定目标文件的格式和编码方式。
2.2 媒体数据的解码和编码
在提取阶段,remuxing.c需要将从源文件中提取出来的音频、视频和字幕等元素进行解码,以便后续的处理和重封装。为了实现这一功能,remuxing.c使用了FFmpeg中的AVCodecContext结构体和相应的解码器函数,例如avcodec_send_packet()和avcodec_receive_frame()等。在重封装阶段,remuxing.c则需要将解码后的音频、视频和字幕等元素进行编码,以便生成目标文件。为此,它使用了AVCodecContext结构体和相应的编码器函数,例如avcodec_send_frame()和avcodec_receive_packet()等。
2.3 数据流的复制和过滤
在提取阶段,remuxing.c需要将从源文件中提取出来的音频、视频和字幕等元素进行复制,以便后续重封装时使用。为此,remuxing.c使用了FFmpeg中的AVPacket结构体和av_packet_copy_props()函数等,实现了数据流的复制操作。
在重封装阶段,remuxing.c还支持对特定元素的过滤和修改。例如,用户可以通过指定命令行参数来删除特定的音频或视频流,或者修改音频采样率等参数。为了实现这一功能,remuxing.c使用了AVFilterGraph结构体和相应的过滤器函数,例如avfilter_graph_create_filter()和av_buffersink_get_frame()等。
2.4 码率控制和优化
在重封装阶段,remuxing.c需要根据用户指定的编码参数和目标文件格式等因素,对音视频数据进行适当的码率控制和优化,以便生成高质量的目标文件。为此,remuxing.c使用了FFmpeg中的AVCodecContext结构体和相关的码率控制函数,例如avcodec_set_bitrate()和av_opt_set()等。
3. 总结
ffmpeg的remuxing.c是一个非常强大和灵活的多媒体文件转换工具,它能够解析多种容器格式,并提取其中的音频、视频和字幕等元素,然后按照用户指定的方式重新封装为目标文件。通过使用FFmpeg中的AVFormatContext、AVCodecContext和AVFilterGraph等结构体,以及相应的解封装、解码、编码、复制和过滤函数,remuxing.c实现了这些功能,并支持多种进阶选项,例如流式处理和码率控制等。因此,remuxing.c是一个非常实用和强大的多媒体工具,适用于各种媒体转换和处理场景。
4.golang重写
这个Go程序使用FFmpeg库来对媒体文件进行重封装,以更改容器格式或编解码器参数。以下是代码的步骤:
(1).导入必要的依赖项,如FFmpeg库和unsafe包。
(2).定义全局变量和函数来设置输出路径、检查目录是否存在、打印Packet信息等。
(3).定义主函数"main",在其中设置各种FFmpeg库的路径、创建输出目录、调用main0函数实现文件重封装。
(4).定义函数"main0",其中初始化输入和输出文件的AVFormatContext,获取输入文件流信息,分配输出文件的上下文并根据输入流创建相应的输出流,将所有流映射到输出上下文,并写入输出文件头部。
(4.1).首先声明需要使用的变量:ifmt_ctx, ofmt_ctx, pkt, in_filename, out_filename, i, stream_index, stream_mapping和stream_mapping_size。
(4.2).打开输入文件并且获取输入文件的流信息。如果无法打开则输出错误并返回ret值。
(4.3).输出input file的音视频流信息。
(4.4).根据输出文件名获取输出文件的 AVFormatContext上下文。
(4.5).分配一个数组来映射输入文件流和输出文件流。如果无法分配,则返回错误码。
(4.6).将输出文件相关的参数初始化为输入文件的参数
(4.7).遍历所有输入流,将输入流映射到相应的输出流并将其添加到输出文件的AVFormatContext中。
(4.8).输出output file的音视频流信息。
(4.9).如果需要,打开输出文件并将其与相应的AVIOContext关联。
(4.10).写入输出文件头部。
(4.11).循环读取输入文件的AVPacket,并根据该Packet所在的输入流信息查找对应的输出流。
(4.12).将时间戳和持续时间转换为输出流格式。
(4.13).将该Packet复制到输出流并写入输出文件。
(4.14).循环结束后,写完输出文件头和文件尾。
(4.15).手动关闭输入文件和释放资源。
(4.16).最后,检查ret值是否小于0且不等于libavutil.AVERROR_EOF,如果是则输出错误信息。
(4.17).在循环中,判断Packet所在的输入流是否为音频、视频或字幕流。如果不是这些流,则将该流映射到输出流-1并跳过。
(4.18).根据流映射数组(stream_mapping)查找对应的输出流,计算时间戳和持续时间等参数,并将Packet复制到输出流并写入输出文件。如果出现错误,输出错误信息并退出循环。
(4.19).释放Packet的资源。
(4.20).写完所有Packet后,写入输出文件的文件尾部。
(4.21).关闭输入文件和输出文件。如果输出文件有相关联的AVIOContext,则同时关闭。
(4.22).最后,如果ret值小于0且不等于libavutil.AVERROR_EOF,则输出错误信息。
(5).循环读取输入文件的AVPacket,检索与当前Packet相关联的输入流和输出流,计算时间戳和持续时间等参数,并将Packet复制到输出流并写入输出文件。
(6).在结束循环后,写入输出文件的文件尾并释放所分配的资源。
总之,这个Go程序使用FFmpeg库来对媒体文件进行重封装,主要实现过程是通过读取输入文件的AVPacket,将其复制到相应的输出文件中,并确保时间戳和持续时间等参数正确设置。
命令如下:
go run ./examples/internalexamples/remuxing/main.go ./resources/big_buck_bunny.mp4 ./out/remuxing.flv
./lib/ffplay ./out/remuxing.flv
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"
)
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 stream_index ffcommon.FInt = 0
var stream_mapping *ffcommon.FInt
var stream_mapping_size ffcommon.FInt = 0
if len(os.Args) < 3 {
fmt.Printf("usage: %s input output\nAPI example program to remux a media file with libavformat and libavcodec.\nThe output format is guessed according to the file extension.\n\n", os.Args[0])
return 1
}
in_filename = os.Args[1]
out_filename = os.Args[2]
ret = libavformat.AvformatOpenInput(&ifmt_ctx, in_filename, nil, nil)
if ret < 0 {
fmt.Printf("Could not open input file '%s'", in_filename)
goto end
}
ret = ifmt_ctx.AvformatFindStreamInfo(nil)
if ret < 0 {
fmt.Printf("Failed to retrieve input stream information")
goto end
}
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
}
stream_mapping_size = int32(ifmt_ctx.NbStreams)
stream_mapping = (*int32)(unsafe.Pointer(libavutil.AvMalloczArray(uint64(stream_mapping_size), 4)))
if stream_mapping == nil {
ret = -libavutil.ENOMEM
goto end
}
ofmt = ofmt_ctx.Oformat
for i = 0; i < int32(ifmt_ctx.NbStreams); i++ {
var out_stream *libavformat.AVStream
in_stream := ifmt_ctx.GetStream(uint32(i))
in_codecpar := in_stream.Codecpar
if in_codecpar.CodecType != libavutil.AVMEDIA_TYPE_AUDIO &&
in_codecpar.CodecType != libavutil.AVMEDIA_TYPE_VIDEO &&
in_codecpar.CodecType != libavutil.AVMEDIA_TYPE_SUBTITLE {
*(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(stream_mapping)) + uintptr(4*i))) = -1
continue
}
*(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(stream_mapping)) + uintptr(4*i))) = stream_index
stream_index++
out_stream = ofmt_ctx.AvformatNewStream(nil)
if out_stream == nil {
fmt.Printf("Failed allocating output stream\n")
ret = libavutil.AVERROR_UNKNOWN
goto end
}
ret = libavcodec.AvcodecParametersCopy(out_stream.Codecpar, in_codecpar)
if ret < 0 {
fmt.Printf("Failed to copy codec parameters\n")
goto end
}
out_stream.Codecpar.CodecTag = 0
}
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 file '%s'", out_filename)
goto end
}
}
ret = ofmt_ctx.AvformatWriteHeader(nil)
if ret < 0 {
fmt.Printf("Error occurred when opening output file\n")
goto end
}
for {
var in_stream, out_stream *libavformat.AVStream
ret = ifmt_ctx.AvReadFrame(&pkt)
if ret < 0 {
break
}
in_stream = ifmt_ctx.GetStream(pkt.StreamIndex)
if pkt.StreamIndex >= uint32(stream_mapping_size) ||
*(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(stream_mapping)) + uintptr(4*pkt.StreamIndex))) < 0 {
pkt.AvPacketUnref()
continue
}
pkt.StreamIndex = uint32(*(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(stream_mapping)) + uintptr(4*pkt.StreamIndex))))
out_stream = ofmt_ctx.GetStream(pkt.StreamIndex)
log_packet(ifmt_ctx, &pkt, "in")
/* copy packet */
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
log_packet(ofmt_ctx, &pkt, "out")
ret = ofmt_ctx.AvInterleavedWriteFrame(&pkt)
if ret < 0 {
fmt.Printf("Error muxing packet\n")
break
}
pkt.AvPacketUnref()
}
ofmt_ctx.AvWriteTrailer()
end:
libavformat.AvformatCloseInput(&ifmt_ctx)
/* close output */
if ofmt_ctx != nil && ofmt.Flags&libavformat.AVFMT_NOFILE == 0 {
libavformat.AvioClosep(&ofmt_ctx.Pb)
}
ofmt_ctx.AvformatFreeContext()
libavutil.AvFreep(uintptr(unsafe.Pointer(&stream_mapping)))
if ret < 0 && ret != libavutil.AVERROR_EOF {
fmt.Printf("Error occurred: %s\n", libavutil.AvErr2str(ret))
return 1
}
return 0
}
func log_packet(fmt_ctx *libavformat.AVFormatContext, pkt *libavcodec.AVPacket, tag string) {
time_base := &fmt_ctx.GetStream(pkt.StreamIndex).TimeBase
fmt.Printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
tag,
libavutil.AvTs2str(pkt.Pts), libavutil.AvTs2timestr(pkt.Pts, time_base),
libavutil.AvTs2str(pkt.Dts), libavutil.AvTs2timestr(pkt.Dts, time_base),
libavutil.AvTs2str(pkt.Duration), libavutil.AvTs2timestr(pkt.Duration, time_base),
pkt.StreamIndex)
}
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
}
}
main0()
}
2023-04-27:用go语言重写ffmpeg的remuxing.c示例。的更多相关文章
- Ubuntu 12.04上安装R语言
Ubuntu 12.04上安装R语言 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ R的安装 sudo gedit /etc/apt/sources. ...
- Ubuntu 18.04 安装配置 go 语言
Ubuntu 18.04 安装配置 go 语言 1.下载 下载 jdk 到 Downloands 文件夹下 cd 进入 /usr/local, 创建 go 文件夹, 然后 cd 进这个文件夹 cd / ...
- 最简单的基于FFmpeg的libswscale的示例(YUV转RGB)
===================================================== 最简单的基于FFmpeg的libswscale的示例系列文章列表: 最简单的基于FFmpeg ...
- 最简单的基于FFmpeg的libswscale的示例附件:测试图片生成工具
===================================================== 最简单的基于FFmpeg的libswscale的示例系列文章列表: 最简单的基于FFmpeg ...
- Ubuntu_16.04 配置 Apache Rwrite URL 重写
Ubuntu Apache配置Rwrite URL重写 0. apache目录
- JS 2016-09-30T22:04:27.5220743+08:00 转换为日期
1.转换代码 new Date(item.CreatedDate).Format("yyyy-MM-dd hh:mm") 2.需要拓展的方法 // 对Date的扩展,将 Date ...
- Ubuntu16.04 部署配置GO语言开发环境 & 注意事项
1. 安装GO 安装go语言包: $ curl -O https://storage.googleapis.com/golang/go1.10.1.linux-amd64.tar.gz 下载完成后 ...
- Ubuntu 18.04 LTS搭建GO语言开发环境
一.下载Go语言安装包 官网下载地址:https://golang.org/dl/,使用tar命令将档案包解压到/usr/local目录中: sudo tar -C /usr/local -xzf g ...
- MingW-v4.8.0+EDE-v13.04 配置使用C语言图形库
From: http://www.cnblogs.com/killerlegend/p/3946768.html Author:KillerLegend Date:2014.8.30 MingW的配置 ...
- ubuntu - 14.04,安装Go语言(谷歌公司开发的一种语言)
Go语言下载地址:https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz 安装: 1,以root身份在shell里执行: ta ...
随机推荐
- Java题目集 函数
6-1 汽车类 (20 分) 编写汽车类,其功能有启动(start),停止(stop),加速(speedup)和减速(slowDown),启动和停止可以改变汽车的状态(on/off),初始时状态为 ...
- MYSQL 变更账号密码
#1 首先找到Mysql[安装的路径],切换到对应的bin目录,例如安装在D盘 C:User\Administrator> d:(输入盘符回车) D:\> cd D:\MySQL\MySQ ...
- HTTP通信基础
1. HTTP通信流程: 1)输入www.baidu.com2)解析成IP地址:192.168.0.13)浏览器通过该IP访问web服务器获取web资源4)再返回给客户端5)最后呈现在用户面前 2. ...
- c++方便的输出vector和map_重载的应用 【python一样写c++、二】
写程序,尤其是调试的时候,会想着直接输出一个map或者vector来调错. 但本来的cout<<没有这种功能.我们就会想了,要是c++能和python一样,直接输出一个列表(vector) ...
- Spring-传统方式(XML)创建webapp
如何搭建一个传统的webapp项目[Java后端] 使用xml 来搭建 SSM 环境,要求 Tomcat 的版本必须在 7 以上 QuickStart 1创建工程 创建一个新模块[普通的 Maven ...
- 一天吃透Git面试八股文
什么是Git? Git是一个版本控制系统,用于跟踪计算机文件的变化.Git是一个跟踪计算机文件变化的版本控制系统,用于帮助协调一个项目中几个人的工作,同时跟踪一段时间的进展.换句话说,我们可以说它是一 ...
- 一篇文章带你快速入门学习RPA
大纲: 什么是RPA? RPA的应用领域有哪些? RPA机器人技术一般用于什么行业? RPA的市场需求是什么? RPA项目的实施过程? RPA的未来趋势怎么样? 什么是RPA? RPA 全称& ...
- 浅谈js防抖和节流
防抖和节流是处理高频触发最常见的优化方式,对性能提升有很大的帮助. 防抖:将多次的高频操作优化为只在最后一次执行,应用场景如:输入框,只需在最后一次输入进行校验即可. 节流:保证每隔一段时间只执行一次 ...
- 如何解决 Iterative 半监督训练 在 ASR 训练中难以落地的问题丨RTC Dev Meetup
前言 「语音处理」是实时互动领域中非常重要的一个场景,在声网发起的「RTC Dev Meetup丨语音处理在实时互动领域的技术实践和应用」活动中,来自微软亚洲研究院.声网.数美科技的技术专家,围绕该话 ...
- RTC月度小报6月丨编程挑战赛圆满收官;声网上市1周年回顾...
本月亮点速览 产品与技术: 声网Agora 实时音视频服务正式上线 HTC VIVE Sync App,支持非 VR 用户 「灵动课堂」发布 1.1.2 版本 「互动直播」6 月共发布两个版,最新版本 ...