2022-04-27:用go语言重写ffmpeg的remuxing.c示例。

news2025/1/20 17:05:23

2022-04-27:用go语言重写ffmpeg的remuxing.c示例。

答案2022-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()
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

构造函数和析构函数

1.构造函数 1.1 .构造函数概括 、构造函数是一个特殊的成员函数&#xff0c;名字与类名相同,创建类类型对象时由编译器自动调用&#xff0c;以保证每个数据成员都有 一个合适的初始值&#xff0c;并且在对象整个生命周期内只调用一次。 构造函数是特殊的成员函数&#xff0c…

简单认识 Postman界面操作

查看本文前 您需要先登录Postman 如果还没有处理好 可以先查看我的文章 Postman登录注册指导 右上角的 Home 代表主页 就是我们现在看到的这个界面 Workspaces 是一个工作空间管理 这里 我们选择进入 我的工作空间 之后 我们所有的接口请求 就都是在这一块完成的 Collection…

ubuntu中安装VMware Tools,实现Windows文件拖入Ubuntu

ubantu作为一款非常好用的Linux发行版本&#xff0c;深受广大开发者的喜爱&#x1f603;&#xff0c;为了开发的方便&#xff0c;人们常常在windows电脑中安装VMware虚拟机来运行Linux系统&#xff0c;我们时常会遇到这样一种情况&#xff1a;无法互传虚拟机与主机文件。 原因就…

终端连接工具Tabby的下载、安装与配置

目录 一、终端连接工具Tabby的下载1.1、Tabby的下载地址1.2、Tabby的下载步骤 二、终端连接工具Tabby的安装三、终端连接工具Tabby的SSH连接四、终端连接工具Tabby的SFTP 传输工具4.1、服务器上的文件传输到本地电脑4.2、本地电脑的文件传输到服务器 五、终端连接工具Tabby的设…

基于网络爬虫和SpringBoot框架的晋江文学小说小型网站项目

一、Python网路爬虫技术的设计与实现 Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架&#xff0c;常可以应用在包括数据挖掘&#xff0c;信息处理或存储历史数据等一系列的程序中。项目中&#xff0c;主要采取Scrapy框架实现一个爬虫&#xff0c;抓…

JavaSE3(4/26)

目录 1.线程的状态 2.线程安全问题 3.synchronized的具体用法 4. 1.线程的状态 首先明白进程的状态:就绪或者阻塞 上述说的就绪和阻塞其实是针对系统中的线程状态(PCB) Java中对于Thread类中的线程的状态进行了进一步的细化 NEW: Thread对象有了,但是线程还没有被执行 TERMI…

RabbitMQ之介绍以及安装

1.1 MQ的相关概念 1.1.1 什么是MQ ​ MQ&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO先入先出&#xff0c;只不过队列中存放的内容是message而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中&#xff0c;MQ…

摄影tips

一、基础知识 相机挡位 A档就是全自动模式或称场景智能自动模式&#xff0c;该拍摄模式下&#xff0c;相机会根据光线和你所拍摄的对象等场景环境自行设置快门、光圈等参数&#xff0c;你不需要调整任何参数&#xff0c;拿起相机对焦按快门就行&#xff0c;也就是AUTO模式。虽…

opengauss编译和使用oracle_fdw

opengauss虽然继承自postgresql9.2&#xff0c;但由于做了魔改&#xff0c;网上通用的从oracle_fdw源码编译安装到postgresql的方法&#xff0c;是否成功的应用到opengauss&#xff0c;并不一定&#xff0c;今天试了一下&#xff0c;参照opengauss官网文档&#xff08;其实写的…

Spring Bean的作用域及生命周期

目录 前言&#xff1a; Bean的作用域&#xff08;Scope&#xff09; 单例模式 原型模式&#xff08;多例作用域&#xff09; 请求作用域&#xff08;request&#xff09; 会话作用域 全局作用域 网络长连接 Spring执行流程 Bean的生命周期 测试 小结&#xff1a; 前…

多模态论文串讲:ALBEF VLMo BLIP CoCa Beit V3

文章目录 前言ALBEF:Align before Fuse: Vision and Language Representation Learning with Momentum Distillation(2021-10)VLMO: Unified Vision-Language Pre-Training with Mixture-of-Modality-Experts(2021-11)relatedmethod BLIP&#xff1a;Bootstrapping Language-Im…

【贴片SD Card介绍】贴片SD Card (LEILONG雷龙科技)

有幸申请到了雷龙科技代理的 贴片 SD Card (SD NAND) 样品&#xff0c;做出测试&#xff0c;分享一下。 型号&#xff1a;CSNP32GCR01-BOW&#xff1b;CSNP4GCR01-BOW 生产方&#xff1a;CS创世半导体 由于是第一次使用贴片类型的 SD Card &#xff0c;可能文章会有较多疏忽。…

【Git】Windows Git和TortoiseGit安装教程(Git2.23.0、TortoiseGit2.8.0、语言包2.8.0)

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

eletron+react+antd+node开发桌面小程序并打包(electron-packager+electron-builder)

首先罗列一下项目中用到的技术&#xff1a; electron, react&#xff0c;antd, typescript, node&#xff0c;及打包命令&#xff1a;pkg&#xff0c;electron-packager&#xff0c;electron-builder及child_process实现多进程 需求&#xff1a;开发一个桌面应用&#xff0c;左…

前端架构师-week3-脚手架执行准备过程实现

目录​​​​​​​​​​​​​​ 脚手架框架代码拆包 import-local应用 检查版本号功能开发&#xff08;require加载资源类型讲解 npmlog封装&#xff09; 最低Node版本检查功能开发 root 账号启动检查和自动降级功能开发 用户主目录检查功能开发 入参检查和 debug…

华为OD机试真题(Java),火星文计算(100%通过+复盘思路)

一、题目描述 已知火星人使用的运算符为#、$&#xff0c;其与地球人的等价公式如下&#xff1a; x#y 2*x3*y4 x$y 3*xy2 其中x、y是无符号整数&#xff1b;地球人公式按C语言规则计算&#xff1b;火星人公式中&#xff0c;$的优先级高于#&#xff0c;相同的运算符&#x…

数字船厂信息化整体解决方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除 数字船厂建设思路 智慧船厂将以信息化为基础、以数据为纽带、以制造为核心、以管理为载体打造新型智慧园区&#xff0c;该智慧园区整合了船厂的安全、环保、能源、安防、应急…

基于 JavaWeb 的用户报名审核平台项目

目录 一、项目的需求&#xff1a; 二、项目的思路流程&#xff1a; 三、项目的框架&#xff08;基于JavaWeb&#xff09;&#xff1a; 四、项目的详细搭建 1、数据库--建库建表 2、JavaBean编写&#xff08;以Status.java为例&#xff09; 3、Dao层&#xff08;StatusDao…

Linux系统与shell编程第一节课

目录 1.1 Linux发展历史 1.2 什么是linux&#xff1f; 1.3 Linux的发行版 Host-Only&#xff08;仅主机模式&#xff09; windows开发 linux服务 区块链&#xff0c; 特点&#xff1a;稳定&#xff0c;安全&#xff0c;可移植性&#xff0c;低资源消耗&#xff0c;开源软…

做工业品行业的财务有多难

财务 做工业品行业的财务需要具备一定的财务和会计知识&#xff0c;以及对行业内经济运作有一定的了解。在处理大量数据和信息方面也需要有较高的能力。此外&#xff0c;工业品行业往往涉及到复杂的生产过程和供应链管理&#xff0c;需要对这些方面有一定的了解和掌握。因此&a…