本文介绍了如何使用FFmpeg库进行媒体文件的转封装操作。注意: 转封装是一种将媒体文件的音视频流重新封装到新的文件中的操作,它通常用于更改媒体文件的容器格式,而不进行重新编码。
步骤1:安装FFmpeg
参见:FFmpeg编译安装,这篇文章不够详细,我现在对编译了解的更多了,后续出一篇详细的。
步骤2:包含FFmpeg头文件
如可以使用类似以下的代码包含头文件:
#include <libavformat/avformat.h>
...
步骤3:初始化FFmpeg
在进行任何FFmpeg操作之前,需要初始化FFmpeg库。可以使用以下代码进行初始化(ffmpeg4.x后可以不再使用):
av_register_all();
步骤4:打开输入文件
使用avformat_open_input()
函数打开,需要转封装的输入文件。如:
AVFormatContext* ifmt_ctx = nullptr;
if (avformat_open_input(&ifmt_ctx, input_filename, nullptr, nullptr) != 0) {
// 处理打开输入文件失败的情况
}
步骤5:获取流信息
使用avformat_find_stream_info()
函数获取输入文件的流信息。如:
if (avformat_find_stream_info(ifmt_ctx, nullptr) < 0) {
// 处理获取流信息失败的情况
}
步骤6:创建输出文件上下文
使用avformat_alloc_output_context2()
函数创建一个新的输出文件上下文。如:
AVFormatContext* ofmt_ctx = nullptr;
if (avformat_alloc_output_context2(&ofmt_ctx, nullptr, nullptr, output_filename) < 0) {
// 处理创建输出文件上下文失败的情况
}
步骤7:将输入文件的音视频流添加到输出文件
使用avformat_new_stream()
函数为每个输入文件的音视频流创建一个对应的输出流。然后,将输入文件的音视频流参数复制到输出流中。如:
for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream* in_stream = ifmt_ctx->streams[i];
AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);
if (out_stream == nullptr) {
// 处理创建新输出流失败的情况
continue;
}
if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
// 处理复制编码参数失败的情况
continue;
}
}
步骤8:打开输出文件
使用avio_open()
函数打开输出文件。如:
if (avio_open(&ofmt_ctx->pb, output_filename, AVIO_FLAG_WRITE) < 0) {
// 处理打开输出文件失败的情况
}
步骤9:写入输出文件头部信息
使用avformat_write_header()
函数写入输出文件的头部信息。以下是一个示例代码片段:
if (avformat_write_header(ofmt_ctx, nullptr) < 0) {
// 处理写入头部信息失败的情况
}
步骤10:转封装音视频包
使用av_interleaved_write_frame()
函数将输入文件的音视频包转封装到输出文件中。以下是一个示例代码片段:
AVPacket packet;
while (av_read_frame(ifmt_ctx, &packet) >= 0) {
// 设置音视频包的流索引
packet.stream_index = stream_mapping[packet.stream_index];
// 将音视频包写入输出文件
if (av_interleaved_write_frame(ofmt_ctx, &packet) < 0) {
// 处理写入音视频包失败的情况
break;
}
av_packet_unref(&packet);
}
请注意,stream_mapping
是一个数组,用于映射输入文件的流索引到输出文件的流索引。
步骤11:写入输出文件尾部信息
使用av_write_trailer()
函数写入输出文件的尾部信息。以下是一个示例代码片段:
if (av_write_trailer(ofmt_ctx) < 0) {
// 处理写入尾部信息失败的情况
}
步骤12:释放资源
在完成转封装操作后,记得释放分配的资源。以下是一些示例代码片段:
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
总结
通过按照上述步骤使用FFmpeg库进行转封装操作,可以将一个媒体文件的音视频流重新封装到一个新的文件中,而不进行重新编码。
转封装完整代码示例基于对以上转封装的流程,我写了一份转封装的代码供大家参考,你可以在终端使用”程序名 *.mp4 *.flv”的命令将一个mp4视频文件转成一个flv视频文件,当然也可以转为其它格式的视频文件。
头文件部分:
#pragma once
#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
struct AVFormatContext;
struct AVPacket;
struct AVOutputFormat;
class Remuxer {
public:
Remuxer();
~Remuxer();
int MediaRemux(const std::string& input_filename, const std::string& output_filename);
private:
int OpenInputFile(const std::string& input_filename, AVFormatContext** ifmt_ctx);
int FindStreamInfo(AVFormatContext* ifmt_ctx);
void DumpInputFormat(AVFormatContext* ifmt_ctx, const std::string& input_filename);
int CreatOutputContext(const std::string& output_filename, AVFormatContext** ofmt_ctx);
int InitStreamMapping(AVFormatContext* ifmt_ctx, int** stream_mapping, int* stream_mapping_size);
int AddOutputStreams(AVFormatContext* ifmt_ctx, AVFormatContext* ofmt_ctx, int* stream_mapping);
void DumpOutputContext(AVFormatContext* ofmt_ctx, const std::string& output_filename);
int OpenOutputFile(AVFormatContext* ofmt_ctx, const std::string& output_filename);
int WriteHeader(AVFormatContext* ofmt_ctx);
int RemuxPackets(AVFormatContext* ifmt_ctx, AVFormatContext* ofmt_ctx, int* stream_mapping, int stream_mapping_size);
void WriteTrailer(AVFormatContext* ofmt_ctx);
private:
AVFormatContext *ifmt_ctx;
AVFormatContext *ofmt_ctx;
AVPacket *pkt;
int *stream_mapping;
const AVOutputFormat *ofmt;
};
源文件部分
#include "remux.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/mem.h>
#include <libavutil/timestamp.h>
}
Remuxer::Remuxer() : ifmt_ctx(nullptr), ofmt_ctx(nullptr),
pkt(nullptr), stream_mapping(nullptr)
{}
Remuxer::~Remuxer()
{
if (pkt)
{
av_packet_free(&pkt);
}
if (ifmt_ctx)
{
avformat_close_input(&ifmt_ctx);
}
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
{
avio_closep(&ofmt_ctx->pb);
}
if (ofmt_ctx)
{
avformat_free_context(ofmt_ctx);
}
av_freep(&stream_mapping);
}
int Remuxer::MediaRemux(const std::string &input_filename, const std::string &output_filename)
{
AVFormatContext* ifmt_ctx = nullptr;
AVFormatContext* ofmt_ctx = nullptr;
int *stream_mapping = nullptr;
int stream_mapping_size = 0;
int ret = OpenInputFile(input_filename, &ifmt_ctx);
if (ret < 0) return ret;
ret = FindStreamInfo(ifmt_ctx);
if (ret < 0)
{
avformat_close_input(&ifmt_ctx);
return ret;
}
DumpInputFormat(ifmt_ctx, input_filename); // 打印输入视频信息
ret = CreatOutputContext(output_filename, &ofmt_ctx);
if (ret < 0)
{
avformat_close_input(&ifmt_ctx);
return ret;
}
ret = InitStreamMapping(ifmt_ctx, &stream_mapping, &stream_mapping_size);
if (ret < 0)
{
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
return ret;
}
ret = AddOutputStreams(ifmt_ctx, ofmt_ctx, stream_mapping);
if (ret < 0) {
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
return ret;
}
DumpOutputContext(ofmt_ctx, output_filename);
ret = OpenOutputFile(ofmt_ctx, output_filename);
if (ret < 0) {
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
return ret;
}
ret = WriteHeader(ofmt_ctx);
if (ret < 0) {
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
return ret;
}
ret = RemuxPackets(ifmt_ctx, ofmt_ctx, stream_mapping, stream_mapping_size);
if (ret < 0) {
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
return ret;
}
WriteTrailer(ofmt_ctx);
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
return 0;
}
int Remuxer::OpenInputFile(const std::string& input_filename, AVFormatContext** ifmt_ctx)
{
int ret = avformat_open_input(ifmt_ctx, input_filename.c_str(), 0, 0);
if (ret < 0)
{
fprintf(stderr, "Colud not open input file '%s'\n", input_filename.c_str());
}
return ret;
}
int Remuxer::FindStreamInfo(AVFormatContext* ifmt_ctx)
{
int ret = avformat_find_stream_info(ifmt_ctx, 0);
if (ret < 0)
{
fprintf(stderr, "Failed to retrieve input stream information\n");
}
return ret;
}
void Remuxer::DumpInputFormat(AVFormatContext* ifmt_ctx, const std::string& input_filename)
{
av_dump_format(ifmt_ctx, 0, input_filename.c_str(), 0);
}
int Remuxer::CreatOutputContext(const std::string &output_filename, AVFormatContext** ofmt_ctx)
{
int ret = avformat_alloc_output_context2(ofmt_ctx, NULL, NULL, output_filename.c_str());
if (!(*ofmt_ctx))
{
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
}
return ret;
}
int Remuxer::InitStreamMapping(AVFormatContext* ifmt_ctx, int** stream_mapping, int* stream_mapping_size)
{
*stream_mapping_size = ifmt_ctx->nb_streams;
*stream_mapping = (int*)av_calloc(*stream_mapping_size, sizeof(**stream_mapping));
if (!(*stream_mapping)) {
return AVERROR(ENOMEM);
}
return 0;
}
int Remuxer::AddOutputStreams(AVFormatContext* ifmt_ctx, AVFormatContext* ofmt_ctx, int* stream_mapping)
{
int stream_idx = 0;
AVOutputFormat *ofmt = ofmt_ctx->oformat;
for (int i = 0; i < ifmt_ctx->nb_streams; i++)
{
AVStream *out_stream;
AVStream *in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecapr = in_stream->codecpar;
if (in_codecapr->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecapr->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecapr->codec_type != AVMEDIA_TYPE_SUBTITLE) {
stream_mapping[i] = -1;
continue;
}
stream_mapping[i] = stream_idx++;
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream)
{
fprintf(stderr, "Failed allocating output stream\n");
return AVERROR_UNKNOWN;
}
int ret = avcodec_parameters_copy(out_stream->codecpar, in_codecapr);
if (ret < 0)
{
fprintf(stderr, "Failed to copy codec parameters\n");
return ret;
}
out_stream->codecpar->codec_tag = 0;
}
return 0;
}
void Remuxer::DumpOutputContext(AVFormatContext* ofmt_ctx, const std::string& output_filename)
{
av_dump_format(ofmt_ctx, 0, output_filename.c_str(), 1);
}
int Remuxer::OpenOutputFile(AVFormatContext* ofmt_ctx, const std::string& output_filename)
{
int ret = 0;
if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
{
ret = avio_open(&ofmt_ctx->pb, output_filename.c_str(), AVIO_FLAG_WRITE);
if (ret < 0)
{
fprintf(stderr, "Could not open output file '%s'\n", output_filename.c_str());
}
}
return ret;
}
int Remuxer::WriteHeader(AVFormatContext* ofmt_ctx)
{
int ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0)
{
fprintf(stderr, "Error occurred when opening output file\n");
}
return ret;
}
int Remuxer::RemuxPackets(AVFormatContext* ifmt_ctx, AVFormatContext* ofmt_ctx, int* stream_mapping, int stream_mapping_size)
{
AVPacket* pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "Could not allocate AVPacket\n");
return -1;
}
while (true) {
int ret = av_read_frame(ifmt_ctx, pkt);
if (ret < 0)
break;
if (pkt->stream_index >= stream_mapping_size ||
stream_mapping[pkt->stream_index] == -1) {
av_packet_unref(pkt);
continue;
}
pkt->stream_index = stream_mapping[pkt->stream_index];
AVStream* in_stream = ifmt_ctx->streams[pkt->stream_index];
AVStream* out_stream = ofmt_ctx->streams[pkt->stream_index];
/* copy packet */
pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
pkt->pos = -1;
ret = av_interleaved_write_frame(ofmt_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error muxing packet\n");
break;
}
av_packet_unref(pkt);
}
av_packet_free(&pkt);
return 0;
}
void Remuxer::WriteTrailer(AVFormatContext* ofmt_ctx)
{
av_write_trailer(ofmt_ctx);
}
主函数部分
#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include "./remux_ex/remux.h"
int main(int argc, char** argv) {
if (argc < 3) {
fprintf(stderr, "Usage: %s <input_file> <output_file>\n", argv[0]);
return 1;
}
const std::string input_filename = argv[1];
const std::string output_filename = argv[2];
Remuxer remuxer;
int ret = remuxer.MediaRemux(input_filename, output_filename);
if (ret != 0) {
fprintf(stderr, "Error occurred during remuxing\n");
return 1;
}
system("pause");
return 0;
}
本文到此结束,如有疑问可以留言或加我微信进行咨询。
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。