最简单的基于 FFmpeg 的音视频转码器(FLV 转 AVI)

Windows下用FFmpeg Batch AV Converter批量转AV到MP4,3分钟搞定上百个文件 本文详细介绍了在Windows系统下使用FFmpeg Batch AV Converter工具批量将AV格式视频转换为MP4的高效方法。通过硬件加速支持、无损转换和后台处理等特性,该工具能在3分钟内处理上百个文件,大幅提升视频转换效率,特别适合IT运维、教育培训和内容创作者。 阅读详情

最简单的基于 FFmpeg 的音视频转码器(FLV 转 AVI)

参考雷霄骅博士的文章,链接:最简单的基于FFMPEG的转码程序

正文

本文介绍一个简单的基于 FFmpeg 的转码器。它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式。转码器在视音频编解码处理的程序中,属于一个比较复杂的东西。因为它结合了视频的解码和编码。一个视频播放器,一般只包含解码功能;一个视频编码工具,一般只包含编码功能;而一个视频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合。

下图例举了一个视频的转码流程。输入视频的封装格式是 FLV,视频编码标准是 H.264,音频编码标准是 AAC;输出视频的封装格式是 AVI,视频编码标准是 MPEG2,音频编码标准是 MP3。从流程中可以看出,首先从输入视频中分离出视频码流和音频压缩码流,然后分别将视频码流和音频码流进行解码,获取到非压缩的像素数据/音频采样数据,接着将非压缩的像素数据/音频采样数据重新进行编码,获得重新编码后的视频码流和音频码流,最后将视频码流和音频码流重新封装成一个文件。

在这里插入图片描述

本文介绍的视频转码器正是使用 FFmpeg 类库从编程的角度实现了上述流程。该例子是从 FFmpeg 的例子改编的,平台是 VC2015,类库版本是 2014.5.6。

流程图

下面附两张使用 FFmpeg 转码视频的流程图。图中使用浅绿色标出了视频的编码、解码函数。

在这里插入图片描述

简单介绍一下流程中各个函数的意义:

  • open_input_file():打开输入文件,并初始化相关的结构体。
  • open_output_file():打开输出文件,并初始化相关的结构体。
  • init_filters():初始化 AVFilter 相关的结构体。
  • av_read_frame():从输入文件中读取一个 AVPacket。
  • avcodec_decode_video2():解码一个视频 AVPacket(存储 H.264 等压缩码流数据)为 AVFrame(存储 YUV 等非压缩的像素数据)。
  • avcodec_decode_audio4():解码一个音频 AVPacket(存储 MP3 等压缩码流数据)为 AVFrame(存储 PCM 采样数据)。
  • filter_encode_write_frame():编码一个 AVFrame。
  • flush_encoder():输入文件读取完毕后,输出编码器中剩余的 AVPacket。

以上函数中 open_input_file(),open_output_file(),init_filters() 中的函数在其他文章中都有所叙述,在这里不再重复:

open_input_file() 可参考:100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)

open_output_file() 可参考:最简单的基于FFMPEG的视频编码器(YUV编码为H.264)

init_filters()可参考:最简单的基于FFmpeg的AVfilter例子(水印叠加)

在这里介绍一下其中编码的函数 filter_encode_write_frame()。filter_encode_write_frame() 函数的流程如下图所示,它完成了视频/音频的编码功能。

PS:视频和音频的编码流程中除了编码函数 avcodec_encode_video2() 和 avcodec_encode_audio2() 不一样之外,其他部分几乎完全一样。

在这里插入图片描述

简单介绍一下 filter_encode_write_frame() 中各个函数的意义:

  • av_buffersrc_add_frame():将解码后的 AVFrame 加入 Filtergraph。

  • av_buffersink_get_buffer_ref():从 Filtergraph 中取一个 AVFrame。

  • avcodec_encode_video2():编码一个视频 AVFrame 为 AVPacket。

  • avcodec_encode_audio2():编码一个音频 AVFrame 为 AVPacket。

  • av_interleaved_write_frame():将编码后的 AVPacket 写入文件。

源程序

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

/**
* 最简单的基于 FFmpeg 的转码器
* Simplest FFmpeg Transcoder
*
* 源程序:
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.youkuaiyun.com/leixiaohua1020
*
* 修改:
* 刘文晨 Liu Wenchen
* 812288728@qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.youkuaiyun.com/ProgramNovice
*
* 本程序实现了视频格式之间的转换。
* 是一个最简单的视频转码程序。
*/

#include "stdafx.h"

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

// 解决报错:'fopen': This function or variable may be unsafe.Consider using fopen_s instead.
#pragma warning(disable:4996)

// 解决报错:无法解析的外部符号 __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 "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/avcodec.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/avcodec.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#ifdef __cplusplus
};
#endif
#endif

static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;

typedef struct FilteringContext
{
	AVFilterContext *buffersrc_ctx;
	AVFilterContext *buffersink_ctx;
	AVFilterGraph *filter_graph;
} FilteringContext;

static FilteringContext *filter_ctx;

static int open_input_file(const char *filename)
{
	int ret;
	unsigned int i;
	ifmt_ctx = NULL;

	ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Can't openinput file.\n");
		return ret;
	}

	ret = avformat_find_stream_info(ifmt_ctx, NULL);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Can't find stream information.\n");
		return ret;
	}
	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		AVStream*stream;
		AVCodecContext *codec_ctx;

		stream = ifmt_ctx->streams[i];
		codec_ctx = stream->codec;
		// Reencode video & audio and remux subtitles etc
		if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
			|| codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			// Open decoder
			ret = avcodec_open2(codec_ctx,
				avcodec_find_decoder(codec_ctx->codec_id), NULL);
			if (ret < 0)
			{
				av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u.\n", i);
				return ret;
			}
		}
	}
	// Print some input information
	av_dump_format(ifmt_ctx, 0, filename, 0);

	return 0;
}
static int open_output_file(const char *filename)
{
	AVStream *out_stream;
	AVStream *in_stream;
	AVCodecContext *dec_ctx, *enc_ctx;
	AVCodec *encoder;
	int ret;
	unsigned int i;
	ofmt_ctx = NULL;

	avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);
	if (!ofmt_ctx)
	{
		av_log(NULL, AV_LOG_ERROR, "Can't create output context.\n");
		return AVERROR_UNKNOWN;
	}
	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		out_stream = avformat_new_stream(ofmt_ctx, NULL);
		if (!out_stream)
		{
			av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream.\n");
			return AVERROR_UNKNOWN;
		}
		in_stream = ifmt_ctx->streams[i];
		dec_ctx = in_stream->codec;
		enc_ctx = out_stream->codec;
		if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
			|| dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			// in this example, we choose transcoding to same codec
			encoder = avcodec_find_encoder(dec_ctx->codec_id);
			/* In this example, we transcode to same properties(picture size,
			* sample rate etc.). These properties can be changed for output
			* streams easily using filters */
			if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
			{
				enc_ctx->height = dec_ctx->height;
				enc_ctx->width = dec_ctx->width;
				enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
				// take first format from list of supported formats
				enc_ctx->pix_fmt = encoder->pix_fmts[0];
				// video time_base can be set to whatever is handy andsupported by encoder
				enc_ctx->time_base = dec_ctx->time_base;
			}
			else
			{
				enc_ctx->sample_rate = dec_ctx->sample_rate;
				enc_ctx->channel_layout = dec_ctx->channel_layout;
				enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
				// take first format from list of supported formats
				enc_ctx->sample_fmt = encoder->sample_fmts[0];
				AVRational time_base = { 1, enc_ctx->sample_rate };
				enc_ctx->time_base = time_base;
			}
			// Third parameter can be used to pass settings to encoder
			ret = avcodec_open2(enc_ctx, encoder, NULL);
			if (ret < 0)
			{
				av_log(NULL, AV_LOG_ERROR, "Can't openvideo encoder for stream #%u.\n", i);
				return ret;
			}
		}
		else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN)
		{
			av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, can't proceed.\n", i);
			return AVERROR_INVALIDDATA;
		}
		else
		{
			// if this stream must be remuxed
			ret = avcodec_copy_context(ofmt_ctx->streams[i]->codec,
				ifmt_ctx->streams[i]->codec);
			if (ret < 0)
			{
				av_log(NULL, AV_LOG_ERROR, "Copying stream context failed.\n");
				return ret;
			}
		}
		if (ofmt_ctx->oformat->flags &AVFMT_GLOBALHEADER)
			enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
	}

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

	if (!(ofmt_ctx->oformat->flags &AVFMT_NOFILE))
	{
		ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't open output file '%s'.", filename);
			return ret;
		}
	}
	// init muxer, write output file header
	ret = avformat_write_header(ofmt_ctx, NULL);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file.\n");
		return ret;
	}
	return 0;
}

static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx,
	AVCodecContext *enc_ctx, const char *filter_spec)
{
	char args[512];
	int ret = 0;
	AVFilter *buffersrc = NULL;
	AVFilter *buffersink = NULL;
	AVFilterContext *buffersrc_ctx = NULL;
	AVFilterContext *buffersink_ctx = NULL;
	AVFilterInOut *outputs = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();
	AVFilterGraph *filter_graph = avfilter_graph_alloc();

	if (!outputs || !inputs || !filter_graph)
	{
		ret = AVERROR(ENOMEM);
		goto end;
	}
	if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
	{
		buffersrc = avfilter_get_by_name("buffer");
		buffersink = avfilter_get_by_name("buffersink");
		if (!buffersrc || !buffersink)
		{
			av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found.\n");
			ret = AVERROR_UNKNOWN;
			goto end;
		}
		_snprintf(args, sizeof(args),
			"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
			dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
			dec_ctx->time_base.num, dec_ctx->time_base.den,
			dec_ctx->sample_aspect_ratio.num,
			dec_ctx->sample_aspect_ratio.den);
		ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
			args, NULL, filter_graph);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't create buffer source.\n");
			goto end;
		}
		ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
			NULL, NULL, filter_graph);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't create buffer sink.\n");
			goto end;
		}
		ret = av_opt_set_bin(buffersink_ctx, "pix_fmts",
			(uint8_t*)&enc_ctx->pix_fmt, sizeof(enc_ctx->pix_fmt),
			AV_OPT_SEARCH_CHILDREN);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't set output pixel format.\n");
			goto end;
		}
	}
	else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)
	{
		buffersrc = avfilter_get_by_name("abuffer");
		buffersink = avfilter_get_by_name("abuffersink");
		if (!buffersrc || !buffersink)
		{
			av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found.\n");
			ret = AVERROR_UNKNOWN;
			goto end;
		}
		if (!dec_ctx->channel_layout)
		{
			dec_ctx->channel_layout = av_get_default_channel_layout(dec_ctx->channels);
		}
		_snprintf(args, sizeof(args),
			"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
			dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate,
			av_get_sample_fmt_name(dec_ctx->sample_fmt),
			dec_ctx->channel_layout);
		ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
			args, NULL, filter_graph);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't create audio buffer source.\n");
			goto end;
		}
		ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
			NULL, NULL, filter_graph);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't create audio buffer sink.\n");
			goto end;
		}
		ret = av_opt_set_bin(buffersink_ctx, "sample_fmts",
			(uint8_t*)&enc_ctx->sample_fmt, sizeof(enc_ctx->sample_fmt),
			AV_OPT_SEARCH_CHILDREN);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't set output sample format.\n");
			goto end;
		}
		ret = av_opt_set_bin(buffersink_ctx, "channel_layouts",
			(uint8_t*)&enc_ctx->channel_layout,
			sizeof(enc_ctx->channel_layout), AV_OPT_SEARCH_CHILDREN);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't set output channel layout.\n");
			goto end;
		}
		ret = av_opt_set_bin(buffersink_ctx, "sample_rates",
			(uint8_t*)&enc_ctx->sample_rate, sizeof(enc_ctx->sample_rate),
			AV_OPT_SEARCH_CHILDREN);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Can't set output sample rate.\n");
			goto end;
		}

	}
	else
	{
		ret = AVERROR_UNKNOWN;
		goto end;
	}
	// Endpoints for the filter graph
	outputs->name = av_strdup("in");
	outputs->filter_ctx = buffersrc_ctx;
	outputs->pad_idx = 0;
	outputs->next = NULL;
	inputs->name = av_strdup("out");
	inputs->filter_ctx = buffersink_ctx;
	inputs->pad_idx = 0;
	inputs->next = NULL;
	if (!outputs->name || !inputs->name)
	{
		ret = AVERROR(ENOMEM);
		goto end;
	}
	ret = avfilter_graph_parse_ptr(filter_graph, filter_spec, &inputs, &outputs, NULL);
	if (ret < 0)
		goto end;
	ret = avfilter_graph_config(filter_graph, NULL);
	if (ret < 0)
		goto end;
	// Fill FilteringContext
	fctx->buffersrc_ctx = buffersrc_ctx;
	fctx->buffersink_ctx = buffersink_ctx;
	fctx->filter_graph = filter_graph;

end:
	avfilter_inout_free(&inputs);
	avfilter_inout_free(&outputs);

	return ret;
}

static int init_filters(void)
{
	const char *filter_spec;
	unsigned int i;
	int ret;

	filter_ctx = (FilteringContext *)av_malloc_array(ifmt_ctx->nb_streams, sizeof(*filter_ctx));
	if (!filter_ctx)
		return AVERROR(ENOMEM);
	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		filter_ctx[i].buffersrc_ctx = NULL;
		filter_ctx[i].buffersink_ctx = NULL;
		filter_ctx[i].filter_graph = NULL;
		if (!(ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
			|| ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO))
			continue;
		if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			// passthrough (dummy) filter for video
			filter_spec = "null";
		}
		else
		{
			// passthrough (dummy) filter for audio
			filter_spec = "anull";
		}
		ret = init_filter(&filter_ctx[i], ifmt_ctx->streams[i]->codec,
			ofmt_ctx->streams[i]->codec, filter_spec);
		if (ret)
			return ret;
	}

	return 0;
}
static int encode_write_frame(AVFrame *filt_frame, unsigned int stream_index, int *got_frame)
{
	int ret;
	int got_frame_local;
	AVPacket enc_pkt;

	int(*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *) =
		(ifmt_ctx->streams[stream_index]->codec->codec_type ==
			AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2;
	if (!got_frame)
		got_frame = &got_frame_local;
	av_log(NULL, AV_LOG_INFO, "Encoding frame.\n");
	// encode filtered frame
	enc_pkt.data = NULL;
	enc_pkt.size = 0;
	av_init_packet(&enc_pkt);
	ret = enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,
		filt_frame, got_frame);
	av_frame_free(&filt_frame);
	if (ret < 0)
		return ret;
	if (!(*got_frame))
		return 0;
	// prepare packet for muxing
	enc_pkt.stream_index = stream_index;
	enc_pkt.dts = av_rescale_q_rnd(enc_pkt.dts,
		ofmt_ctx->streams[stream_index]->codec->time_base,
		ofmt_ctx->streams[stream_index]->time_base,
		(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
	enc_pkt.pts = av_rescale_q_rnd(enc_pkt.pts,
		ofmt_ctx->streams[stream_index]->codec->time_base,
		ofmt_ctx->streams[stream_index]->time_base,
		(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
	enc_pkt.duration = av_rescale_q(enc_pkt.duration,
		ofmt_ctx->streams[stream_index]->codec->time_base,
		ofmt_ctx->streams[stream_index]->time_base);
	av_log(NULL, AV_LOG_DEBUG, "Muxing frame.\n");
	// mux encoded frame
	ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);
	return ret;
}
static int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index)
{
	int ret;
	AVFrame*filt_frame;
	av_log(NULL, AV_LOG_INFO, "Pushing decoded frame to filters.\n");
	// push the decoded frame into the filtergraph
	ret = av_buffersrc_add_frame_flags(filter_ctx[stream_index].buffersrc_ctx,
		frame, 0);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph.\n");
		return ret;
	}
	// pull filtered frames from the filtergraph
	while (1)
	{
		filt_frame = av_frame_alloc();
		if (!filt_frame) 
		{
			ret = AVERROR(ENOMEM);
			break;
		}
		av_log(NULL, AV_LOG_INFO, "Pulling filtered frame from filters.\n");
		ret = av_buffersink_get_frame(filter_ctx[stream_index].buffersink_ctx,
			filt_frame);
		if (ret < 0) 
		{
			/* if no more frames for output - returns AVERROR(EAGAIN)
			* if flushed and no more frames for output - returns AVERROR_EOF
			* rewrite retcode to 0 to show it as normal procedure completion
			*/
			if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
				ret = 0;
			av_frame_free(&filt_frame);
			break;
		}
		filt_frame->pict_type = AV_PICTURE_TYPE_NONE;
		ret = encode_write_frame(filt_frame, stream_index, NULL);
		if (ret < 0)
			break;
	}
	return ret;
}
static int flush_encoder(unsigned int stream_index)
{
	int ret;
	int got_frame;

	if (!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities & CODEC_CAP_DELAY))
		return 0;
	while (1)
	{
		av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder.\n", stream_index);
		ret = encode_write_frame(NULL, stream_index, &got_frame);
		if (ret < 0)
			break;
		if (!got_frame)
			return 0;
	}
	return ret;
}

int main(int argc, char* argv[])
{
	int ret;
	AVPacket packet;
	AVFrame *frame = NULL;
	enum AVMediaType type;
	unsigned int stream_index;
	unsigned int i;
	int got_frame;
	int(*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket*);

	if (argc != 3)
	{
		av_log(NULL, AV_LOG_ERROR, "Usage: %s <input file> <output file>.\n", argv[0]);
		return 1;
	}

	av_register_all();
	avfilter_register_all();

	// Open input file
	ret = open_input_file(argv[1]);
	if (ret < 0)
		goto end;

	// Open output file
	ret = open_output_file(argv[2]);
	if (ret < 0)
		goto end;

	// Init filters
	ret = init_filters();
	if (ret < 0)
		goto end;

	// Read all packets
	while (1)
	{
		// Get a packet
		ret = av_read_frame(ifmt_ctx, &packet);
		if (ret < 0)
			break;
		stream_index = packet.stream_index;
		type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type;
		av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream index %u.\n",
			stream_index);
		if (filter_ctx[stream_index].filter_graph) 
		{
			av_log(NULL, AV_LOG_DEBUG, "Going to reencode and filter the frame.\n");
			frame = av_frame_alloc();
			if (!frame) 
			{
				ret = AVERROR(ENOMEM);
				break;
			}
			// 转换 DTS/PTS
			packet.dts = av_rescale_q_rnd(packet.dts,
				ifmt_ctx->streams[stream_index]->time_base,
				ifmt_ctx->streams[stream_index]->codec->time_base,
				(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			packet.pts = av_rescale_q_rnd(packet.pts,
				ifmt_ctx->streams[stream_index]->time_base,
				ifmt_ctx->streams[stream_index]->codec->time_base,
				(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 :
				avcodec_decode_audio4;
			ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame,
				&got_frame, &packet);
			if (ret < 0) 
			{
				av_frame_free(&frame);
				av_log(NULL, AV_LOG_ERROR, "Decoding failed.\n");
				break;
			}
			if (got_frame)
			{
				frame->pts = av_frame_get_best_effort_timestamp(frame);
				ret = filter_encode_write_frame(frame, stream_index);
				av_frame_free(&frame);
				if (ret < 0)
					goto end;
			}
			else
			{
				av_frame_free(&frame);
			}
		}
		else
		{
			// remux this frame without reencoding
			packet.dts = av_rescale_q_rnd(packet.dts,
				ifmt_ctx->streams[stream_index]->time_base,
				ofmt_ctx->streams[stream_index]->time_base,
				(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			packet.pts = av_rescale_q_rnd(packet.pts,
				ifmt_ctx->streams[stream_index]->time_base,
				ofmt_ctx->streams[stream_index]->time_base,
				(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
			ret = av_interleaved_write_frame(ofmt_ctx, &packet);
			if (ret < 0)
				goto end;
		}
		av_free_packet(&packet);
	}
	// flush filters and encoders
	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		// flush filter
		if (!filter_ctx[i].filter_graph)
			continue;
		ret = filter_encode_write_frame(NULL, i);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Flushing filter failed.\n");
			goto end;
		}
		// flush encoder
		ret = flush_encoder(i);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Flushing encoder failed.\n");
			goto end;
		}
	}

	// Write file trailer
	av_write_trailer(ofmt_ctx);

end:
	av_free_packet(&packet);
	av_frame_free(&frame);

	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		avcodec_close(ifmt_ctx->streams[i]->codec);
		if (ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && ofmt_ctx->streams[i]->codec)
			avcodec_close(ofmt_ctx->streams[i]->codec);
		if (filter_ctx && filter_ctx[i].filter_graph)
			avfilter_graph_free(&filter_ctx[i].filter_graph);
	}

	av_free(filter_ctx);
	avformat_close_input(&ifmt_ctx);
	if (ofmt_ctx &&!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
		avio_close(ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Error occurred.\n");
		return -1;
	}

	system("pause");
	return 0;
}

结果

默认情况下运行程序,会将“cuc_ieschool.ts”转换为“cuc_ieschool.avi”。调试的时候,可以修改“配置属性->调试->命令参数”中的参数,即可改变转码的输入输出文件。

在这里插入图片描述

程序的运行结果如下:

在这里插入图片描述

输入文件:cuc_ieschool.ts

在这里插入图片描述

输出文件:cuc_ieschool.avi

在这里插入图片描述

工程文件下载

GitHub:UestcXiye / Simplest-FFmpeg-Transcoder

优快云:Simplest FFmpeg Transcoder.zip

参考链接

  1. 最简单的基于FFMPEG的转码程序
Android FFmpeg音视频解码播放 FFmpeg(FastForward Mpeg)是一款遵循GPL的开源软件,在音视频处理方面表现十分优秀,几乎囊括了现存所有的视音频格式的编码,解码、转码、混合、过滤及播放。同时也是一款跨平台的软件,完美兼容Linux、Windows、Mac OSX等平台。FFmpeg:由命令行组成,用于多媒体格式转换 FFplay:基于FFmpeg开源代码库libraries做的多媒体播放器 FFprobe:基于FFmpeg做的多媒体流分析器。 阅读详情

相关推荐

ffmpeg学习笔记-初识ffmpeg

ffmpeg用来对音视频进行处理,那么在使用ffmpeg前就需要ffmpeg有一个大概的了解,这里使用雷神的ppt素材进行整理,以便于复习。

yinshipin007的博客 825

2023停止摆烂,掌握 FFmpeg 技能,走上高级感之路

FFmpeg 是一个开源的多媒体处理框架,用于处理音频、视频和流媒体等各种多媒体数据。它提供了一组强大的命令行工具,可用于音视频的录制、转码、编辑和播放等操作。FFmpeg 支持多种视频编解码器和音频编解码器,以及各种常见的多媒体格式。本文介绍了 FFmpeg 是一个开源的多媒体处理框架,用于处理音频、视频和流媒体等各种多媒体数据。它提供了一组强大的命令行工具,可用于音视频的录制、转码、编辑和播放等操作,支持多种视频编解码器和音频编解码器,以及各种常见的多媒体格式。

weixin_45947430的博客 1785

FFmpeg入门详解之74:FFmpeg转码器Java版之需求规格说明书

音视频通用转码系统。视频(Video)泛指将一系列静态影像以电信号方式加以捕捉,纪录,处理,储存,传送,与重现的各种技术。连续的图像变化每秒超过24帧(frame)画面以上时,根据视觉暂留原理,人眼无法辨别单幅的静态画面;看上去是平滑连续的视觉效果,这样连续的画面叫做视频。视频格式(Video Format):特定格式的多媒体文件,主要包括:AVI, MP4, RMVB, MOV, 3GP, FLV, ……音频是个专业术语,只是储存在计算机里的声音。它泛指人类能够听到的所有声音,它可能包括噪音等。

福优学苑@音视频+流媒体 1101

多媒体处理神器!FFmpeg Batch AV Converter:小白也能玩转专业级影音转换

▍零门槛的影音转换利器作为基于FFmpeg引擎的免费工具,FFmpeg Batch AV Converter完美兼容Windows/Linux系统(后者通过Wine运行),用可视化界面打破命令行技术壁垒。无需记忆复杂代码,拖拽点击即可完成专业级影音处理,真正实现"复杂功能,简单操作"。▍七大核心功能全解析→ 闪电级批量转码:多线程引擎榨干硬件性能,百个文件同步处理→ 智能流程向导:三步完成复杂操作,新手秒变熟手→ 格式通吃王:解码上百种音视频格式,应对各种场景需求。

weixin_46004477的博客 1179

FFmpeg转码

转码在音视频处理中,是最常见的一种,原始视频的格式各不相同,为方便用户使用和平台兼容,转码就显得很重要的。通过转码,我们可以把不同格式,分辨率,码率的转换成统一的,便于使用的格式。 一个简单的基于FFmpeg的转码器。它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式。转码器在视音频编解码处理的程序中,属于一个比较复杂的东西。因为它结合了视频的解码和...

程序猿老樊的博客 654

Qt+FFmpeg桌面音视频转码器开发实战指南

音视频转码是多媒体处理的基础能力,涉及编解码原理、硬件加速机制与跨平台GUI集成。FFmpeg作为工业级多媒体框架,提供H.264/H.265/AV1等编码支持及NVENC/QSV/CUDA硬件加速能力;Qt则凭借成熟的C++ GUI生态和信号槽机制,实现拖拽导入、实时进度反馈、批量队列管理等工程化需求。相比命令行工具的交互缺失或Web方案的沙箱限制,Qt+FFmpeg组合在企业内网部署、监控录像统一处理、会议录屏标准化输出等场景中展现出高可控性与可维护性。本文聚焦于Qt 5.15.2与FFmpeg 6.x

weixin_27014595的博客 246

【亲测免费】 FFmpeg Batch AV Converter 常见问题解决方案

FFmpeg Batch AV Converter 是一个开源项目,它为 FFmpeg 提供了一个图形用户界面(GUI),使得用户可以通过简单的鼠标点击操作来利用 FFmpeg 的强大功能。这个项目支持 Windows 和 Linux 系统(通过 Wine-Mono)。它允许用户进行无限的单文件或多文件批量编码,适用于从新手到高级用户的各种需求。主要编程语言是 C#,它使用了 Windows 窗体

gitblog_00331的博客 1493

FFmpeg视频解码实战演示项目

FFmpeg 是一个开源的多媒体处理框架,广泛应用于音视频编解码、转码、流媒体处理等领域。其架构由多个核心库组成,包括(处理容器格式)、libavcodec(编解码核心)、libavutil(通用工具函数)等,形成一个高度模块化、可扩展的系统。在现代音视频开发中,FFmpeg 被用于视频播放器、直播推拉流、视频编辑软件、安防监控系统等多个场景。掌握其整体架构和模块功能,有助于开发者快速构建高性能、跨平台的多媒体应用,是音视频开发的基石技能。

weixin_30021053的博客 434

免费视频格式转换工具,一键批量处理,小白也能秒会

该软件最引人注目的特色在于其卓越的批量处理能力,用户只需简单拖拽,即可将成批视频文件导入转换队列,系统会自动完成后续处理工作,极大节省了操作时间。针对专业用户的需求,软件提供了深度的参数调节功能,包括视频分辨率、帧率和比特率等关键指标的精确控制,让输出成品完全符合预期标准。为了照顾到不同层次用户的使用需求,FFmpeg Batch AV Converter采用了清晰直观的界面设计,即便是首次接触的用户也能迅速上手。同时,其稳定的性能表现和出色的转换质量,让它成为了视频处理工作中不可或缺的得力助手。

dntkorg的博客 581

掌握FFmpeg4.2.2:视频处理的终极解决方案

FFmpeg是一个强大的跨平台多媒体框架,用于处理音视频数据流。它支持几乎所有的音视频格式和协议,适用于从简单的格式转换到复杂的流处理。

weixin_32747681的博客 1120

经典的FFmpeg窗口界面程序

得使用命令来使用的FFmpeg对于很多人是个大麻烦,窗口界面的来了。

最简单的基于FFMPEG的转码程序

本文介绍一个简单的基于FFmpeg的转码器。它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式。转码器在视音频编解码处理的程序中,属于一个比较复杂的东西。因为它结合了视频的解码和编码。一个视频播放器,一般只包含解码功能;一个视频编码工具,一般只包含编码功能;而一个视频转码器,则...

chuifuhuo6864的博客 190

EDF Browser使用说明和技巧

edf browser使用说明和一些小技巧

Erhu_666的博客 8336

ffmpeg nginx flv_RTMP/HLS/HTTP-FLV流媒体服务器分析

目录整天框架分析rtmp拉流分析rtmp推流分析hls拉流分析hsl推流分析http-flv拉流分析http-flv推流分析ffmpeg转码分析首屏秒开技术分析1.1 RTMP的简介RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。RTMP是一种设计...

weixin_39671405的博客 140

Linux单独编译设备树,开发者分享 | Petalinux 工程中设备树的介绍

本文来自XILINX嵌入式产品应用工程师 Terry Ni设备树是 Petalinux kernel 的关键组件,接下来以 2020.1 版本为例,为大家介绍一下在Xilinx Petalinux 工程中的设备树是如何产生,配置以及修改的。Petalinux 工程中设备树的位置当我们创建了一个 petalinux 工程后(以zcu102为例),在编译的时候,会自动生成对应的设备树文件。这些设备树的...

weixin_28877107的博客 2609

Oracle GoldenGate (OGG) 实战:从安装到故障排查的完整避坑指南

本文提供了一份详尽的Oracle GoldenGate实战指南,涵盖从环境准备、核心进程配置到性能调优与故障排查的全过程。重点解析了OGG安装与部署中的常见陷阱,并分享了确保数据同步链路稳定运行的高级运维技巧与常见故障处理方法,帮助技术人员高效避坑。

weixin_29230649的博客 380

x-cmd-pkg | ffmpeg - 音视频处理领域中常用的开源转换工具

ffmpeg 是音视频处理领域中常用的开源转换工具。以强大的功能、广泛的格式支持和丰富的参数调节在处理音视频格式的任务中得到了广泛的使用。FFmpeg 是由 Fabrice Bellard 于 2000 年发起的开源多媒体框架,使用 C 语言编写。“FF” 代表"fast forward",意为高效处理,而 “mpeg” 的则是指 MPEG 组织 (负责制定影音压缩及传输标准的国际标准化组织)。FFmpeg 不仅提供一系列音视频处理库,还以此为基础提供了命令行工具ffmpeg。

edwinjhlee的博客 742

UNet改进(12):融合小波变换与注意力机制的图像分割新方法

本文提出了一种改进的WaveletAttentionUNet架构,通过结合小波变换和注意力机制来增强传统UNet的多尺度特征提取能力。该网络使用分组卷积模拟小波分解,将输入通道扩展为4个子带(LL、LH、HL、HH),并通过通道注意力机制自适应加权各子带特征。网络保留了UNet的编码器-解码器结构,在关键位置集成了WaveletAttention模块。实验证明该方法在医学图像分割等任务中能更好地保持边缘细节和复杂纹理特征,同时保持计算效率。

2501_91516851的博客 759

HTML视频处理:FFmpeg+Python+Miniconda方案

通过FFmpeg、Python与Miniconda的高效组合,实现本地化、自动化、可复现的视频处理流程。方案支持格式转换、批量处理、Web集成与环境隔离,适用于在线教育、AI数据准备和监控摘要等真实场景,兼顾性能、稳定与工程可维护性。

weixin_42509507的博客 1003

SCI投稿Title Page、Cover letter、Highlights、Manuscript模板

(简要介绍一下论文的创新性). I hope this paper issuitable for “Journal Name”.版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.youkuaiyun.com/weixin_48615832/article/details/120317336。

qq_15963745的博客 3700

R语言机器学习算法实战系列(十一)MLP算法分类器 (Multi-Layer Perceptrons)

本文介绍了多层感知器(MLP)分类算法的原理和计算步骤,并通过R语言实现MLP模型。MLP是一种前馈神经网络,包含输入层、隐藏层和输出层,通过激活函数引入非线性,利用前向传播和反向传播进行训练。文章详细描述了数据预处理、数据切割、模型构建、预测和评估等步骤,并提供了乳腺癌数据集(BreastCancer_clean.csv)的下载链接。通过R包如neuralnet和caret,读者可以逐步实现MLP模型,并评估其分类性能。

专注生信领域 936
上一篇: Leetcode3069. 将元素分配到两个数组中 I
下一篇: 最简单的基于 FFmpeg 的内存读写的例子:内存转码器
UestcXiye
博客等级 码龄8年 9031粉丝 1306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值