当前位置: 首页 > 网络编程 > python

使用Python实现视频拼接效果<

时间:2025-03-18 14:19:18 python 我要投稿
这篇文章主要为大家详细介绍了使用Python实现视频拼接效果的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

方法一:

视频较长,分辨率较大,这个效果很好,不耗用内存 ffmpeg

 import subprocess
import glob
import os
from natsort import natsorted
base_dir = r'C:\Users\Administrator\Videos\shuiyin\result'
output_file = r'output_shuiyin.mp4'
video_paths = glob.glob(base_dir + '/*.mp4')
video_paths = natsorted(video_paths)
with open('file_list.txt', 'w') as f:
    for file in video_paths:
        f.write(f"file '{file}'\n")
ffmpeg_command = [
    'ffmpeg',
    '-f', 'concat',           # 指定拼接模式
    '-safe', '0',             # 允许绝对路径
    '-i', 'file_list.txt',    # 输入的文件列表
    '-c:v', 'libx264',        # 使用 libx264 编码器
    '-c:a', 'aac',            # 使用 aac 编码音频
    '-strict', 'experimental',# 使用实验性编码
    output_file               # 输出文件路径
]
subprocess.run(ffmpeg_command, check=True)
 
print(f"视频拼接完成,输出文件:{output_file}")

方法二:

利用imageio,适合视频较短

import glob
from natsort import natsorted
from moviepy.editor import VideoFileClip, concatenate_videoclips
import glob
import os.path
 
from natsort import natsorted
import cv2
import imageio
 
if __name__ == '__main__':
    #内存
    base_dir =r"C:\Users\Administrator\Videos\shuiyin\0127"
    base_dir =r'C:\Users\Administrator\Videos\shuiyin\result'
    output_path = "pinjie_shuiyin.mp4"
    video_paths =glob.glob(base_dir +'/*.mp4')
    video_paths=natsorted(video_paths)
    imgs=[]
    res = []
    for file in video_paths:
        cap_a = cv2.VideoCapture(file)  # 打开视频B
        fps = cap_a.get(cv2.CAP_PROP_FPS)
        frame_count = 0
        print(file)
        while True:
            ret, frame_a = cap_a.read()
            if not ret:
                break  # 如果没有读取到帧,则跳出循环
            res.append(cv2.cvtColor(frame_a, cv2.COLOR_BGR2RGB))
            frame_count += 1  # 释放视频资源
        cap_a.release()
    imageio.mimsave(output_path, res, "mp4", fps=fps, macro_block_size=None)
 

方法三:

使用FFmpeg进行视频拼接,主要以两个文件为例进行合并,并且只转换其中的视频流

vector<string> fileList = { url_origin,url_add };//这是两个文件
//获得原始输入视频文件编码等信息
const AVOutputFormat* ofmt = NULL;//输出格式
AVFormatContext* ifmt_ctx = NULL, * ofmt_ctx = NULL;//视频数据维护对象
AVPacket* pkt = NULL;//数据包

int ret;//函数执行返回码
int stream_index;//数据流索引

pkt = av_packet_alloc();//初始化数据包结构
if (!pkt)
{
	return;
}

if ((ret = avformat_open_input(&ifmt_ctx, url_origin, 0, 0) < 0))
{
	goto end;//打开文件失败
}

//获得输出文件名
string out_file;
auto name = ifmt_ctx->iformat->name;//自动识别文件的封装类型
//hevc只能使用MP4或者hevc封装才能完成转换,其余封装报错,因为这里进行了自动识别可以不用管具体格式
out_file.replace(out_file.find('.')+1, 3, name);
const char* out_filename = out_file.c_str();

//根据第一个文件获得其中的编码等参数,这里要求两个文件的编码格式一样就是因为在写入文件时用的是相同的配置没有进行转码等操作
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)
{
	goto end;
}

avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx)
{
	goto end;
}

ofmt = ofmt_ctx->oformat;
//查找视频流并复制视频流的参数到输出流
for (int i = 0; i < ifmt_ctx->nb_streams; ++i)
{
	AVStream* in_stream = ifmt_ctx->streams[i];
	AVCodecParameters* in_codecpar = in_stream->codecpar;
	if (in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO)//非视频流跳过
	{
		continue;
	}
	AVStream* out_stream = avformat_new_stream(ofmt_ctx, NULL);//创建输出流
	if (!out_stream)
	{
		goto end;
	}
	ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);//复制解码器参数
	if (ret < 0)
	{
		goto end;
	}
	out_stream->time_base = in_stream->time_base;//复制时间基
	stream_index = i;
	out_stream->codecpar->codec_tag = 0;
	break;
}
avformat_close_input(&ifmt_ctx);//关闭文件

//打开输出文件
if (!(ofmt->flags & AVFMT_NOFILE))
{
	ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
	if (ret < 0)
	{
		goto end;
	}
}

ret = avformat_write_header(ofmt_ctx, NULL);//写入头信息,如编码等内容
if (ret < 0)
{
	goto end;
}

int64_t i = 0;//用于计算时间戳,同时也是帧数
int64_t p_max_dts = 0;//用于拼文件的时间戳

for (int index = 0; index < fileList.size(); ++index)//遍历文件
{
	if ((ret = avformat_open_input(&ifmt_ctx, fileList[index].c_str(), 0, 0)) < 0)
	{
		goto end;
	}

	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)//查找文件流信息
	{
		goto end;
	}

	//对流直接进行转写
	while (1)
	{
		AVStream* in_stream, * out_stream;
		ret = av_read_frame(ifmt_ctx, pkt);
		if (ret < 0)
		{
			break;
		}
		pkt->stream_index = stream_index;//视频流编号
		//这里做一个提示,因为上述的例子只有视频没有音频所以不会越界,如果存在多种流的这里需要看一下你new了几个流,是否会越界
		in_stream = ifmt_ctx->streams[stream_index];
		out_stream = ofmt_ctx->streams[stream_index];

		//这里要对时间戳进行处理,否则写入的时候会失败
		//单帧时长
		int64_t frameDuration = av_rescale_q(1, av_inv_q(in_stream->time_base), in_stream->r_frame_rate);
		//将单帧的时间从输入流转化到输出流时间
		int64_t _t = av_rescale_q(frameDuration, in_stream->time_base, out_stream->time_base);

		//计算时间戳,并进行累计以推算后面的时间戳
		p_max_dts = _t * (i);
		pkt->dts = p_max_dts;
		pkt->pts = pkt->dts;
		
		//如果音视频都需要写入可能需要这个函数:av_interleaved_write_frame,他会进行交叉写入
		//pkt现在是空的,这个函数会获得pkt内容的所有权并重置,因此不需要unref,但是write_frame情况不同,需要手动释放
		ret = av_write_frame(ofmt_ctx, pkt);//直接将包写入输出文件不进行解码
		av_packet_unref(pkt);
		if (ret < 0)
		{
			break;
		}
		++i;
	}

	//关闭文件
	avformat_close_input(&ifmt_ctx);
}

av_write_trailer(ofmt_ctx);//写文件尾

end:
	av_packet_free(&pkt);//这里传指针,因为要将pkt设为null
	avformat_close_input(&ifmt_ctx);//同理
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
	{
		avio_closep(&ofmt_ctx->pb);//avio打开要释放
	}
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF)
	{
		return;//异常结束
	}

这个示例可以完成视频流的复制拼接,是一个比较简单的示例,要求文件编码等信息必须一致,不进行转码,速度比较快。

到此这篇关于使用Python实现视频拼接效果的文章就介绍到这了,更多相关Python视频拼接内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!