最简单的基于 FFmpeg 的封装格式转换器(无编解码)

news2024/11/24 11:25:22

最简单的基于 FFmpeg 的封装格式转换器(无编解码)

  • 最简单的基于 FFmpeg 的封装格式转换器(无编解码)
    • 正文
    • 结果
    • 工程文件下载

最简单的基于 FFmpeg 的封装格式转换器(无编解码)

参考雷霄骅博士的文章,链接:最简单的基于FFMPEG的封装格式转换器(无编解码)

正文

本文介绍一个最简单的基于 FFmpeg 的封装格式转换器。所谓的封装格式转换,就是在 AVI,FLV,MKV,MP4 这些格式之间转换(对应 .avi,.flv,.mkv,.mp4 文件)。

需要注意的是,本程序并不进行视音频的编码和解码工作。而是直接将视音频压缩码流从一种封装格式文件中获取出来然后打包成另外一种封装格式的文件。

传统的转码程序工作原理如下图所示:

在这里插入图片描述

上图例举了一个举例:FLV(视频:H.264,音频:AAC)转码为 AVI(视频:MPEG2,音频:MP3)的例子。可见视频转码的过程通俗地讲相当于把视频和音频重新“录”了一遍。

本程序的工作原理如下图所示:

在这里插入图片描述

由图可见,本程序并不进行视频和音频的编解码工作,因此本程序和普通的转码软件相比,有以下两个特点:

  1. 处理速度极快。视音频编解码算法十分复杂,占据了转码的绝大部分时间。因为不需要进行视音频的编码和解码,所以节约了大量的时间。
  2. 视音频质量无损。因为不需要进行视音频的编码和解码,所以不会有视音频的压缩损伤。

下面附上基于 FFmpeg 的 Remuxer 的流程图。图中使用浅红色标出了关键的数据结构,浅蓝色标出了输出视频数据的函数。可见整个程序包含了对两个文件的处理:读取输入文件(位于左边)和写入输出文件(位于右边)。中间使用了一个 avcodec_copy_context() 拷贝输入的 AVCodecContext 到输出的 AVCodecContext。

请添加图片描述

简单介绍一下流程中关键函数的意义:

输入文件操作:

  • avformat_open_input():打开输入文件,初始化输入视频码流的 AVFormatContext。

  • av_read_frame():从输入文件中读取一个 AVPacket。

输出文件操作:

  • avformat_alloc_output_context2():初始化输出视频码流的 AVFormatContext。

  • avformat_new_stream():创建输出码流的 AVStream。

  • avcodec_copy_context():拷贝输入视频码流的 AVCodecContex 的数值 t 到输出视频的 AVCodecContext。

  • avio_open():打开输出文件。

  • avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说 MPEG2TS)。

  • av_interleaved_write_frame():将 AVPacket(存储视频压缩码流数据)写入文件。

  • av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说 MPEG2TS)。

源代码:

// Simplest FFmpeg Remuxer.cpp : 定义控制台应用程序的入口点。
//


/*
* 最简单的基于FFmpeg的封装格式转换器
* Simplest FFmpeg Remuxer
*
* 源程序:
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 修改:
* 刘文晨 Liu Wenchen
* 812288728@qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.csdn.net/ProgramNovice
*
* 本程序实现了视频封装格式之间的转换。
* 需要注意的是本程序并不改变视音频的编码格式。
*
* This software converts a media file from one container format
* to another container format without encoding/decoding video files.
*/

#include "stdafx.h"

#include <stdio.h>
#include <stdlib.h>

// 解决报错:无法解析的外部符号 __imp__fprintf,该符号在函数 _ShowError 中被引用
#pragma comment(lib, "legacy_stdio_definitions.lib")
extern "C"
{
	// 解决报错:无法解析的外部符号 __imp____iob_func,该符号在函数 _ShowError 中被引用
	FILE __iob_func[3] = { *stdin, *stdout, *stderr };
}

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
// Windows
extern "C"
{
#include "libavformat/avformat.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endif

int main(int argc, char* argv[])
{
	AVOutputFormat *ofmt = NULL;
	// 输入对应一个 AVFormatContext,输出对应一个 AVFormatContext
	AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
	AVPacket pkt;

	const char *in_filename = "cuc_ieschool.flv"; // 输入文件名
	const char *out_filename = "cuc_ieschool.mp4"; // 输出文件名

	int ret;
	int frame_index;
	int i;

	//if (argc < 3)
	//{
	//	printf("usage: %s input output\n"
	//		"Remux a media file with libavformat and libavcodec.\n"
	//		"The output format is guessed according to the file extension.\n"
	//		, argv[0]);
	//	return 1;
	//}

	av_register_all();

	// 输入
	ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0);
	if (ret < 0)
	{
		printf("Could not open input file.\n");
		goto end;
	}

	ret = avformat_find_stream_info(ifmt_ctx, 0);
	if (ret < 0)
	{
		printf("Failed to retrieve input stream information.\n");
		goto end;
	}

	// Print some input information
	av_dump_format(ifmt_ctx, 0, in_filename, 0);

	// 输出
	avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
	if (ofmt_ctx == NULL)
	{
		printf("Could not create output context.\n");
		ret = AVERROR_UNKNOWN;
		goto end;
	}
	ofmt = ofmt_ctx->oformat;

	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		// 根据输入流创建输出流
		AVStream *in_stream = ifmt_ctx->streams[i];
		AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
		if (out_stream == NULL)
		{
			printf("Failed allocating output stream.\n");
			ret = AVERROR_UNKNOWN;
			goto end;
		}

		// 拷贝输入视频码流的 AVCodecContex 的数值 t 到输出视频的 AVCodecContext
		ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
		if (ret < 0)
		{
			printf("Failed to copy context from input to output stream codec context.\n");
			goto end;
		}
		out_stream->codec->codec_tag = 0;
		if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
		{
			out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
		}
	}

	// Print some output information
	av_dump_format(ofmt_ctx, 0, out_filename, 1);

	// Open output file
	if (!(ofmt->flags & AVFMT_NOFILE))
	{
		ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
		if (ret < 0)
		{
			printf("Could not open output file '%s'.\n", out_filename);
			goto end;
		}
	}
	// Write file header
	ret = avformat_write_header(ofmt_ctx, NULL);
	if (ret < 0)
	{
		printf("Error occurred when opening output file.\n");
		goto end;
	}

	frame_index = 0;

	while (1)
	{
		AVStream *in_stream, *out_stream;
		// 获取一个 AVPacket
		ret = av_read_frame(ifmt_ctx, &pkt);
		if (ret < 0)
		{
			break;
		}

		in_stream = ifmt_ctx->streams[pkt.stream_index];
		out_stream = ofmt_ctx->streams[pkt.stream_index];
		// copy packet
		// 转换 PTS/DTS
		pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
		pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
		pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
		pkt.pos = -1;

		// 将 AVPacket 写入文件
		ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
		if (ret < 0)
		{
			printf("Error muxing packet.\n");
			break;
		}
		printf("Write %8d frames to output file.\n", frame_index);
		av_free_packet(&pkt);
		frame_index++;
	}

	// Write file trailer
	av_write_trailer(ofmt_ctx);


end:
	avformat_close_input(&ifmt_ctx);
	// close output
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
	{
		avio_close(ofmt_ctx->pb);
	}
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF)
	{
		printf("Error occurred.\n");
		return -1;
	}

	system("pause");
	return 0;
}

本程序可以直接在 VS 2015 上运行。

结果

运行程序,将输入 flv 文件转封装为 mp4 文件,以下是其信息:

在这里插入图片描述

下图显示了一个测试的输入文件的视音频参数。

在这里插入图片描述

下图显示了输出文件的视音频参数。

在这里插入图片描述

可以看出除了视频的封装格式从flv转换成了mp4,其他有关视音频编码的参数没有任何变化。

工程文件下载

GitHub:UestcXiye / Simplest-FFmpeg-Remuxer

CSDN:Simplest FFmpeg Remuxer.zip

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

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

相关文章

软件工程师,为什么不喜欢关电脑

概述 你是否注意到&#xff0c;软件工程师们似乎从不关电脑&#xff0c;也不喜欢关电脑&#xff1f;别以为他们是电脑“上瘾”&#xff0c;或是沉迷于电脑&#xff0c;这一现象背后蕴含着多种实际原因。 1、代码保存与恢复。 在编写代码过程中&#xff0c;遇到问题时可能会暂时…

Java 基于 SpringBoot+Vue 的酒店管理系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

C++数据结构与算法——栈与队列

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

二维数组及函数的非函数实现

2024年2月14日 1.请编程实现二维数组的杨慧三角 #include<stdio.h> #include<stdlib.h> #include<string.h> void Yanghui(int n,int (*p)[n]) {for(int i0;i<n;i){for(int j0;j<i;j){if(j0||ij){*(*(pi)j)1;}else{*(*(pi)j)*(*(pi-1)j-1)*(*(pi-1)j)…

SpringBoot整合GateWay(详细配置)

前言 在Spring Boot中整合Spring Cloud Gateway是一个常见的需求&#xff0c;尤其是当需要构建一个微服务架构的应用程序时。Spring Cloud Gateway是Spring Cloud生态系统中的一个项目&#xff0c;它提供了一个API网关&#xff0c;用于处理服务之间的请求路由、安全、监控和限流…

Ubuntu20.04 安装jekyll

首先使根据官方文档安装&#xff1a;Jekyll on Ubuntu | Jekyll • Simple, blog-aware, static sites 如果没有报错&#xff0c;就不用再继续看下去了。 我这边在执行gem install jekyll bundler时报错&#xff0c;所以安装了rvm&#xff0c;安装rvm可以参考这篇文章Ubuntu …

【网站项目】155在线考试与学习交流网页平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Ubuntu 20.04 安装RVM

RVM是管理Ruby版本的工具,使用RVM可以在单机上方便地管理多个Ruby版本。 下载安装脚本 首先使下载安装脚本 wget https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer 如果出现了 Connection refused 的情况, 可以考虑执行以下命令修改dns,再执…

2024HVV | 12款开源渗透测试工具(非常详细)零基础入门到精通,收藏这一篇就够了

回顾过去&#xff0c;黑客入侵异常困难&#xff0c;需要大量手动操作。然而&#xff0c;如今&#xff0c;一套自动化测试工具让渗透测试人员变身“半机械人”&#xff0c;能够比以往任何时候都更轻松地完成更多测试。以下12款开源渗透测试工具&#xff0c;可以帮助渗透测试人员…

羊大师发现,奶源对羊奶会产生如此重大的影响

羊大师发现&#xff0c;奶源对羊奶会产生如此重大的影响 羊奶对于人类来说是一种极其营养丰富的食物&#xff0c;日常生活中必不可少。而对于儿童来说&#xff0c;除母乳外&#xff0c;奶粉也是一种非常重要的营养补充品。成人饮用的羊奶对于羊奶的来源和产地要求较低&#xf…

【c++】list详细讲解

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟悉list库 > 毒鸡汤&#xff1a;你的脸上云淡…

五分钟搭建本地大数据集群

引言 刚接触大数据以及部分接触大数据多年的伙伴可能从来没有自己搭建过一套属于自己的大数据集群&#xff0c;今天就花点时间聊聊怎么快速搭建一套属于自己、且可用于操作、调试的大数据集群 正文 本次搭建的组件都有以下服务以及对应的版本 hadoop&#xff08;3.2.4&…

MySQL运行错误:‘mysql‘不是内部或外部命令,也不是可运行程序或批处理文

主要原因是&#xff1a;没有将mysql安装目录下的bin目录&#xff0c;添加到系统变量中 编辑系统环境变量 双击Path即可 下一步 记得每一步点击确定就好啦。 下面验证一下是否成功呢&#xff1f; 输入命令符(V是大写的哦~&#xff09; mysql -V 以上就是成功啦&#xff01…

【VTKExamples::PolyData】第三十三期 MiscCellData

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享VTK样例MiscCellData,了解如何创建PolyData数据,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. MiscCellData /…

用Java实现简单的图书管理系统

目录 1.总体框架 2.book包 Books类 booklist类 3.operation包 IO接口&#xff1a; addbooks类&#xff1a; borrowbooks类&#xff1a; delbooks类&#xff1a; returnbooks类&#xff1a; exit类&#xff1a; 4.user包 user类 Adminuser类&#xff08;难点&#…

阅读笔记(BMSB 2018)Video Stitching Based on Optical Flow

参考文献 Xie C, Zhang X, Yang H, et al. Video Stitching Based on Optical Flow[C]//2018 IEEE International Symposium on Broadband Multimedia Systems and Broadcasting (BMSB). IEEE, 2018: 1-5. 摘要 视频拼接在计算机视觉中仍然是一个具有挑战性的问题&#xff0…

StarRocks表设计——分区分桶与副本数

目录 一、数据分布 1.1 概述 1.2 数据分布方式 1.2.1 Round-Robin 1.2.2 Range 1.2.3 List 1.2.4 Hash 1.3 StarRocks的数据分布方式 1.3.1 不分区 Hash分桶 1.3.2 Range分区Hash分桶 三、分区 3.1 分区概述 3.2 创建分区 3.2.1 手动创建分区 3.2.2 批量创建分区…

2.1.1 摄像头

摄像头 更多内容&#xff0c;请关注&#xff1a; github&#xff1a;https://github.com/gotonote/Autopilot-Notes.git 摄像头是目前自动驾驶车中应用和研究最广泛的传感器&#xff0c;其采集图像的过程最接近人类视觉系统。基于图像的物体检测和识别技术已经相当成熟&#…

探讨深度学习

深度学习 深度学习概述进展崛起框架 主页传送门&#xff1a;&#x1f4c0; 传送 深度学习 概述 深度学习是机器学习领域的一个分支&#xff0c;它是一种基于人工神经网络的学习方法&#xff0c;旨在让 计算机模仿人类大脑的神经结构和学习方式&#xff0c;从大量数据中学习并…

SIFT 2D/3D检测原理

一、SIFT 2D 二、SIFT 3D SIFT 3D关键点检测以及SAC-IA粗配准-CSDN博客