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()
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/15683.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SAM的二次开发案例收集分享

一、AnyLabeling[1]——制作人&#xff1a;vietanhdev AnyLabeling LabelImg Labelme Improved UI Autolabeling AnyLabeling软件是一个集成了YOLO、Segment Anything模型&#xff08;AI支持&#xff09;的高效数据标注工具&#xff0c;它可以通过点击目标的方式完成目标检…

Windows Vscode 远程连接Ubuntu, vscode检测到#include错误请更新includePath的解决方法

&#xff08;闭坑&#xff09;首先&#xff0c;我们要明白一点&#xff0c;就是我们在windows用vscode 远程连接了Ubuntu后&#xff0c;我们的Vscode的环境就是Ubuntu,不再是window了&#xff0c;所以出现问题&#xff0c;应该想到的是Ubuntu上的环境问题&#xff0c;而不是win…

机器学习与深度学习——通过SVM线性支持向量机分类鸢尾花数据集iris求出错误率并可视化

线性支持向量机 先来看一下什么叫数据近似线性可分&#xff0c;如下图所示&#xff0c;蓝色圆点和红色圆点分别代表正类和负类&#xff0c;显然我们不能找到一个线性的分离超平面将这两类完全正确的分开&#xff1b;但是如果将数据中的某些特异点(黑色箭头指向的点)去除之后&a…

根据cadence设计图学习硬件知识day06 了解一些电源转化芯片和 稳压器 和 开关芯片

1. TPL920 (高精度线性稳压器) 1.1.TPL920 介绍 TPL920系列产品是2A大电流、6μVRMS低噪声、高PSRR、高精度线性稳压器&#xff0c;通常具有在2A负载条件下的110 mV超低电压降。这TPL920系列产品同时支持固定输出电压范围从0.8伏到3.95伏&#xff0c;输出电压可调范围为0.8V至…

肝一肝设计模式【四】-- 建造者模式

系列文章目录 肝一肝设计模式【一】-- 单例模式 传送门 肝一肝设计模式【二】-- 工厂模式 传送门 肝一肝设计模式【三】-- 原型模式 传送门 肝一肝设计模式【四】-- 建造者模式 传送门 文章目录 系列文章目录前言一、什么是建造者模式二、举个栗子三、静态内部类写法四、开源框…

内存取证小练习-基础训练

这是题目和wolatility2.6的链接 链接&#xff1a;https://pan.baidu.com/s/1wNYJOjLoXMKqbGgpKOE2tg?pwdybww 提取码&#xff1a;ybww --来自百度网盘超级会员V4的分享 压缩包很小&#xff0c;题目也比较简单基础&#xff0c;可以供入门使用 1&#xff1a;Which volatility…

【QT5:CAN卡通信的上位机-代码练习-收发数据+布局+引用外部库+基础样例(1)】

【QT5:CAN卡通信的上位机-代码练习-收发数据布局引用外部库基础样例1】 1、概述2、实验环境3、自我总结和提升4、事先声明5、效果展示6、代码编写过程&#xff08;1&#xff09;操作步骤部分1、新建工程2、加入外部库&#xff0c;并且加入qt工程中3、ui页面布局4、代码练习5、运…

急急急!Kafka Topic 资源权限紧张怎么办?

我们都知道 Kafka 的 topic 资源比较“贵”&#xff0c;所以一般会给项目 topic 权限限制&#xff0c;按需申请。Milvus 会在建新表时自动申请 kafka topic 资源&#xff0c;这时候自动申请不到怎么办&#xff1f;手动配置 topic 要符合什么规范才能被 Milvus 使用&#xff1f;…

聚观早报|特斯拉向第三方电动车开放充电桩;Epic 诉苹果垄断败诉

今日要闻&#xff1a;特斯拉向第三方电动车开放充电桩&#xff1b;我国全面实现不动产统一登记&#xff1b;Epic 诉苹果垄断败诉&#xff1b;腾讯大股东Naspers再减持近79万股&#xff1b;星巴克中国门店将超过万家 特斯拉向第三方电动车开放充电桩 近日&#xff0c;特斯拉官方…

AlgoC++第七课:手写Matrix

目录 手写Matrix前言1. 明确需求2. 基本实现2.1 创建矩阵2.2 外部访问2.3 <<操作符重载 3. 矩阵运算3.1 矩阵标量运算3.2 通用矩阵乘法3.3 矩阵求逆 4. 完整示例代码总结 手写Matrix 前言 手写AI推出的全新面向AI算法的C课程 Algo C&#xff0c;链接。记录下个人学习笔记…

Mysql安装

目录&#xff1a; 1.Mysql安装 2. 安装MySQL出现1045错误 3.更改数据库编码格式 1.mysql图文安装教程(详细说明) &#xff11;、打开下载的mysql安装文件mysql-5.5.27-win32.zip&#xff0c;双击解压缩&#xff0c;运行“setup.exe” &#xff12;、选择安装类型&#xff0c;有…

【计算机网络】1.1——因特网概述

因特网概述&#xff08;了解&#xff09; 网络、互联网和因特网 网络由若干结点和连接这些结点的链路组成 多个网络还可以通过路由器互连起来&#xff0c;互联网是"网络的网络“ internet 和 Internet internet&#xff08;互联网或互连网&#xff09;是通用名词 泛指…

最近部门新的00后真是卷王,工作没1年,入职18K

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。 这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作都没1年&#xff0c;到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 …

生成式模型与辨别式模型

分类模型可以分为两大类:生成式模型与辨别式模型。本文解释了这两种模型类型之间的区别&#xff0c;并讨论了每种方法的优缺点。 辨别式模型 辨别式模型是一种能够学习输入数据和输出标签之间关系的模型&#xff0c;它通过学习输入数据的特征来预测输出标签。在分类问题中&…

Vue+Echart实现利用率表盘效果【组件已封装,可直接使用】

效果演示 当利用超过70%&#xff08;可以自行设置&#xff09;&#xff0c;表盘变红 组件 里面对应两个图片资源&#xff0c;panelBackground_red.png 和 panelBackground_green.png&#xff0c;请前往百度网盘进行下载。如果喜欢其他颜色&#xff0c;可以使用.psd来修改导出…

基于html+css的图展示43

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

vCenter(PSC)正常更改或重置administrator@vsphere.local用户的密码方法

1. 正常更改administratorvsphere.local用户密码 在vCenter界面中选择“菜单”下的“系统管理”&#xff0c;如下图所示&#xff1a; 然后在Single Sign On下的用户和组中&#xff0c;选择“vsphere.local”域&#xff0c;再对Administrator用户进行编辑&#xff0c;即可进行…

Unsupervised Learning of Depth and Ego-Motion from Video 论文精读

视频中深度和自我运动的无监督学习 摘要 我们提出了一个无监督学习框架&#xff0c;用于从非结构化视频序列中进行单眼深度和相机运动估计。与其他工作[10&#xff0c;14&#xff0c;16]一样&#xff0c;我们使用端到端的学习方法&#xff0c;将视图合成作为监督信号。与之前…

[openwrt] valgrind定位内存泄漏

目录 要求 valgrind 简介 工具介绍 linux程序的内存布局 内存检查的原理 valgrind的使用 使用举例 内存泄漏 内存越界 内存覆盖 Linux分配虚拟内存&#xff08;申请内存&#xff09;的两种方式 brk和mmap 要求 被调试程序带有-g参数编译&#xff0c;携带debug参数…

Ubuntu22.04部署Pytorch2.0深度学习环境

文章目录 安装Anaconda创建新环境安装Pytorch2.0安装VS CodeUbuntu下实时查看GPU状态的方法小实验&#xff1a;Ubuntu、Windows10下GPU训练速度对比 Ubuntu安装完显卡驱动、CUDA和cudnn后&#xff0c;下面部署深度学习环境。 &#xff08;安装Ubuntu系统、显卡驱动、CUDA和cudn…