使用Python實(shí)現(xiàn)視頻拼接效果
方法一:
視頻較長,分辨率較大,這個效果很好,不耗用內(nèi)存 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',# 使用實(shí)驗(yàn)性編碼 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__': #內(nèi)存 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 # 如果沒有讀取到幀,則跳出循環(huán) 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進(jìn)行視頻拼接,主要以兩個文件為例進(jìn)行合并,并且只轉(zhuǎn)換其中的視頻流
vector<string> fileList = { url_origin,url_add };//這是兩個文件 //獲得原始輸入視頻文件編碼等信息 const AVOutputFormat* ofmt = NULL;//輸出格式 AVFormatContext* ifmt_ctx = NULL, * ofmt_ctx = NULL;//視頻數(shù)據(jù)維護(hù)對象 AVPacket* pkt = NULL;//數(shù)據(jù)包 int ret;//函數(shù)執(zhí)行返回碼 int stream_index;//數(shù)據(jù)流索引 pkt = av_packet_alloc();//初始化數(shù)據(jù)包結(jié)構(gòu) 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或者h(yuǎn)evc封裝才能完成轉(zhuǎn)換,其余封裝報(bào)錯,因?yàn)檫@里進(jìn)行了自動識別可以不用管具體格式 out_file.replace(out_file.find('.')+1, 3, name); const char* out_filename = out_file.c_str(); //根據(jù)第一個文件獲得其中的編碼等參數(shù),這里要求兩個文件的編碼格式一樣就是因?yàn)樵趯懭胛募r用的是相同的配置沒有進(jìn)行轉(zhuǎn)碼等操作 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; //查找視頻流并復(fù)制視頻流的參數(shù)到輸出流 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);//創(chuàng)建輸出流 if (!out_stream) { goto end; } ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);//復(fù)制解碼器參數(shù) if (ret < 0) { goto end; } out_stream->time_base = in_stream->time_base;//復(fù)制時間基 stream_index = i; out_stream->codecpar->codec_tag = 0; break; } avformat_close_input(&ifmt_ctx);//關(guān)閉文件 //打開輸出文件 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);//寫入頭信息,如編碼等內(nèi)容 if (ret < 0) { goto end; } int64_t i = 0;//用于計(jì)算時間戳,同時也是幀數(shù) 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; } //對流直接進(jìn)行轉(zhuǎn)寫 while (1) { AVStream* in_stream, * out_stream; ret = av_read_frame(ifmt_ctx, pkt); if (ret < 0) { break; } pkt->stream_index = stream_index;//視頻流編號 //這里做一個提示,因?yàn)樯鲜龅睦又挥幸曨l沒有音頻所以不會越界,如果存在多種流的這里需要看一下你new了幾個流,是否會越界 in_stream = ifmt_ctx->streams[stream_index]; out_stream = ofmt_ctx->streams[stream_index]; //這里要對時間戳進(jìn)行處理,否則寫入的時候會失敗 //單幀時長 int64_t frameDuration = av_rescale_q(1, av_inv_q(in_stream->time_base), in_stream->r_frame_rate); //將單幀的時間從輸入流轉(zhuǎn)化到輸出流時間 int64_t _t = av_rescale_q(frameDuration, in_stream->time_base, out_stream->time_base); //計(jì)算時間戳,并進(jìn)行累計(jì)以推算后面的時間戳 p_max_dts = _t * (i); pkt->dts = p_max_dts; pkt->pts = pkt->dts; //如果音視頻都需要寫入可能需要這個函數(shù):av_interleaved_write_frame,他會進(jìn)行交叉寫入 //pkt現(xiàn)在是空的,這個函數(shù)會獲得pkt內(nèi)容的所有權(quán)并重置,因此不需要unref,但是write_frame情況不同,需要手動釋放 ret = av_write_frame(ofmt_ctx, pkt);//直接將包寫入輸出文件不進(jìn)行解碼 av_packet_unref(pkt); if (ret < 0) { break; } ++i; } //關(guān)閉文件 avformat_close_input(&ifmt_ctx); } av_write_trailer(ofmt_ctx);//寫文件尾 end: av_packet_free(&pkt);//這里傳指針,因?yàn)橐獙kt設(shè)為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;//異常結(jié)束 }
這個示例可以完成視頻流的復(fù)制拼接,是一個比較簡單的示例,要求文件編碼等信息必須一致,不進(jìn)行轉(zhuǎn)碼,速度比較快。
到此這篇關(guān)于使用Python實(shí)現(xiàn)視頻拼接效果的文章就介紹到這了,更多相關(guān)Python視頻拼接內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python函數(shù)裝飾器常見使用方法實(shí)例詳解
這篇文章主要介紹了Python函數(shù)裝飾器常見使用方法,結(jié)合實(shí)例形式分析了Python函數(shù)裝飾器的概念、原理、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-03-03用Python的pandas框架操作Excel文件中的數(shù)據(jù)教程
這篇文章主要介紹了用Python的pandas框架操作Excel文件中的數(shù)據(jù)教程,包括單位格式轉(zhuǎn)換、分類匯總等基本操作,需要的朋友可以參考下2015-03-03Python采集C站熱榜數(shù)據(jù)實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了Python采集C站熱榜數(shù)據(jù)實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05python中文件變化監(jiān)控示例(watchdog)
這篇文章主要介紹了python中文件變化監(jiān)控示例(watchdog),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10python3+PyQt5實(shí)現(xiàn)文檔打印功能
這篇文章主要為大家詳細(xì)介紹了python3+PyQt5實(shí)現(xiàn)文檔打印功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04python從list列表中選出一個數(shù)和其對應(yīng)的坐標(biāo)方法
今天小編就為大家分享一篇python從list列表中選出一個數(shù)和其對應(yīng)的坐標(biāo)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07