使用boost::mp11实现序列截取功能

C++使用boost::mp11截取序列
403 篇文章 ¥29.90 ¥99.00
本文介绍了如何在C++编程中利用boost::mp11库的mp_take函数来截取序列,通过示例展示了截取过程,并提到可以结合库中的其他函数进行序列操作。

使用boost::mp11实现序列截取功能

在C++编程中,序列截取是指从一个序列中截取指定长度的一段子序列。boost::mp11库提供了mp_take函数可以方便地实现这个功能。

下面是一个简单的示例程序,用于演示如何使用boost::mp11的mp_take函数来截取序列:

#include <iostream>
#include <type_traits>
#include <boost/mp11.hpp>

import json import random import re import shutil import time from pathlib import Path from moviepy.editor import (VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips) from utils import file_utils def get_subdirectory_videos(subdir): """获取指定子文件夹中的所有视频文件(适配 video_with_audio_sub_xxxx.mp4 格式)""" subdir = Path(subdir) if not subdir.is_dir(): return [] # 查找当前子文件夹中的视频文件(不递归到更深层次) video_files = list(subdir.glob("video_with_audio_sub_*.mp4")) if not video_files: print(f"子文件夹 {subdir} 中未找到符合格式的视频文件") return [] # 按文件名中的数字序号排序 try: # 调整正则表达式以匹配包含effect的文件名 return sorted(video_files, key=lambda x: int(re.search(r'video_with_audio_sub_(?:effect_)?(\d+)', x.stem).group(1))) except (AttributeError, ValueError) as e: print(f"警告: 子文件夹 {subdir} 中视频文件排序失败 - {e},将使用默认排序") return sorted(video_files) def get_subdirectories(parent_dir): """获取指定目录下的所有直接子文件夹""" parent_dir = Path(parent_dir) if not parent_dir.is_dir(): return [] # 只返回直接子文件夹,不包括更深层次的 return [d for d in parent_dir.iterdir() if d.is_dir()] def safe_close(clip): """安全关闭视频片段,避免资源泄漏""" try: if clip: # 先关闭音频流 if hasattr(clip, 'audio') and clip.audio: try: clip.audio.close() except: pass # 再关闭视频流 try: clip.close() except: pass except Exception as e: print(f"关闭视频片段时出错: {e}") def get_background_music_list(): """获取背景音乐文件路径列表""" # 背景音乐位于与agents同级的config文件夹下 config_dir = Path(__file__).parent.parent / "config" # 支持的音乐文件扩展名 music_extensions = ['.mp3', '.wav', '.ogg', '.flac', '.m4a'] music_files = [] # 查找所有背景音乐文件 for ext in music_extensions: # 查找以 back_music 开头的文件 music_files.extend(list(config_dir.glob(f"back_music*{ext}"))) # 也可以查找其他命名模式的背景音乐 music_files.extend(list(config_dir.glob(f"background*{ext}"))) music_files.extend(list(config_dir.glob(f"bgm*{ext}"))) # 去重并排序 music_files = list(set(music_files)) music_files.sort() if not music_files: print(f"警告: 未找到背景音乐文件,搜索目录: {config_dir}") return [] print(f"找到 {len(music_files)} 个背景音乐文件:") for music in music_files: print(f" - {music.name}") return [str(music) for music in music_files] def get_all_transition_sounds(): """获取所有可用的转场音效""" # 转场音效位于与agents同级的bgm文件夹下 bgm_dir = Path(__file__).parent.parent / "cut_bgm" if not bgm_dir.exists() or not bgm_dir.is_dir(): print(f"警告: 未找到转场音效文件夹 {bgm_dir}") return [] # 获取所有音频文件 audio_extensions = ['.mp3', '.wav', '.ogg', '.flac'] sound_files = [] for ext in audio_extensions: sound_files.extend(list(bgm_dir.glob(f"*{ext}"))) if not sound_files: print(f"警告: 转场音效文件夹 {bgm_dir} 中未找到音频文件") return [] return [str(sound) for sound in sound_files] def boost_audio(audio_clip, gain_db=9.0): # 增加原音频增益(从6dB提高到9dB) """增加音频增益""" try: # 将分贝转换为倍数 gain_factor = 10 ** (gain_db / 20) return audio_clip.volumex(gain_factor) except Exception as e: print(f"音频增益处理失败: {e}") return audio_clip def process_background_music(video_clip, music_path, music_volume=0.07): """处理背景音乐:调整音量,根据视频时长循环或截断""" try: # 加载背景音乐 music = AudioFileClip(music_path) # 获取视频时长和原音频 video_duration = video_clip.duration original_audio = video_clip.audio # 应用音量调整 - 使用与第一个代码相同的volumex方法 music = music.volumex(music_volume) print(f"背景音乐音量已调整为原音量的 {music_volume * 100}%") # 处理背景音乐时长 music_duration = music.duration if music_duration >= video_duration: # 音乐时长足够,截断到视频时长 music = music.subclip(0, video_duration) else: # 音乐时长不足,循环播放 num_loops = int(video_duration / music_duration) + 1 music_clips = [music for _ in range(num_loops)] music = concatenate_audioclips(music_clips).subclip(0, video_duration) # 混合原音频和背景音乐 if original_audio is not None: # 增加原音频增益(使用更新后的9dB增益) boosted_original_audio = boost_audio(original_audio) final_audio = CompositeAudioClip([boosted_original_audio, music]) else: final_audio = music return final_audio except Exception as e: print(f"处理背景音乐时出错: {e}") return None def add_audio_crossfade(clips, crossfade_duration=0.6): """ 在视频片段之间添加音频交叉淡入淡出效果,避免突然的音频截断 crossfade_duration: 交叉淡入淡出的时长(秒) """ if len(clips) <= 1: return clips # 只有一个片段,不需要处理 processed_clips = [] # 处理第一个片段 first_clip = clips[0] if first_clip.audio and first_clip.duration > crossfade_duration: # 对第一个片段的音频末尾添加淡出效果 audio = first_clip.audio fade_out_audio = audio.audio_fadeout(crossfade_duration) first_clip = first_clip.set_audio(fade_out_audio) processed_clips.append(first_clip) # 处理中间片段 for i in range(1, len(clips) - 1): current_clip = clips[i] if current_clip.audio and current_clip.duration > crossfade_duration * 2: # 对中间片段的音频添加淡入淡出效果 audio = current_clip.audio fade_audio = audio.audio_fadein(crossfade_duration) fade_audio = fade_audio.audio_fadeout(crossfade_duration) current_clip = current_clip.set_audio(fade_audio) processed_clips.append(current_clip) # 处理最后一个片段 - 添加淡入和淡出 last_clip = clips[-1] if last_clip.audio: # 先添加淡入 if last_clip.duration > crossfade_duration: audio = last_clip.audio fade_audio = audio.audio_fadein(crossfade_duration) # 再添加淡出 if last_clip.duration > crossfade_duration * 2: fade_audio = fade_audio.audio_fadeout(crossfade_duration) last_clip = last_clip.set_audio(fade_audio) processed_clips.append(last_clip) return processed_clips def normalize_audio_levels(clips, target_dBFS=-14): # 提高目标音量(从-16dBFS提高到-14dBFS) """ 标准化所有片段的音频电平,使其音量一致 target_dBFS: 目标音量级别(分贝全标度),提高目标值使声音更大 """ processed_clips = [] for clip in clips: if clip.audio: # 计算当前音频的最大音量 try: current_dBFS = clip.audio.max_volume() # 计算需要的音量调整 if current_dBFS != 0: # 避免除以零 change_in_dBFS = target_dBFS - current_dBFS # 应用音量调整 normalized_audio = clip.audio.volumex(10 ** (change_in_dBFS / 20)) clip = clip.set_audio(normalized_audio) except Exception as e: print(f"标准化音频电平时出错: {e}") # 出错时保持原音频不变 processed_clips.append(clip) return processed_clips def add_transition_effects(clips): """ 为视频片段添加转场音效: - 第一个和第二个视频之间必须随机添加一个转场特效 - 其他视频每三个视频后随机添加一个转场特效 - 转场音效添加在后一个视频的第一秒 """ if len(clips) <= 1: return clips # 只有一个片段,不需要转场 processed_clips = [] all_transition_sounds = get_all_transition_sounds() has_transition_sounds = len(all_transition_sounds) > 0 # 先添加第一个片段 processed_clips.append(clips[0]) # 遍历剩余片段,处理转场 for i in range(1, len(clips)): current_clip = clips[i] transition_sound = None add_transition = False # 转场逻辑: # 1. 第一个和第二个视频之间(i=1)必须添加转场 # 2. 之后每三个视频后(i=4,7,10...)随机添加转场 if has_transition_sounds: if i == 1: # 第一个转场点:必须添加 add_transition = True elif (i - 1) % 3 == 0: # 每三个视频后的转场点:50%概率添加 add_transition = random.random() < 0.3 # 如果需要添加转场且有可用音效 if add_transition and has_transition_sounds: # 随机选择一个转场音效 selected_sound_path = random.choice(all_transition_sounds) print(f"在第 {i} 个视频前添加转场音效: {Path(selected_sound_path).name}") try: # 加载转场音效 transition_sound = AudioFileClip(selected_sound_path) # 限制转场音效时长不超过1秒 if transition_sound.duration > 1: transition_sound = transition_sound.subclip(0, 1) # 调整转场音效音量(使用volumex方法,与第一个代码保持一致) transition_sound = transition_sound.volumex(0.15) except Exception as e: print(f"加载转场音效时出错: {e}") transition_sound = None # 处理当前片段,添加转场音效(如果有) if transition_sound: # 确保当前视频足够长,可以添加1秒的转场 if current_clip.duration < 1: # 视频太短,直接添加,不添加转场音效 processed_clips.append(current_clip) try: transition_sound.close() except: pass continue # 截取当前视频的前1秒 current_clip_first_part = current_clip.subclip(0, 1) # 在当前视频的前1秒添加转场音效 final_audio = CompositeAudioClip([ current_clip_first_part.audio, transition_sound.set_start(0) # 从0秒开始播放转场音效 ]) # 创建带转场音效的前1秒视频片段 current_clip_with_transition = current_clip_first_part.set_audio(final_audio) # 创建当前视频除了前1秒的部分 current_clip_main_part = current_clip.subclip(1, current_clip.duration) # 添加处理后的当前片段 processed_clips.append(current_clip_with_transition) processed_clips.append(current_clip_main_part) # 关闭转场音效 try: transition_sound.close() except: pass else: # 不添加转场音效,直接添加当前片段 processed_clips.append(current_clip) return processed_clips def concatenate_videos(video_files, output_path, music_path=None, progress_callback=None): """拼接所有已带字幕的视频文件并添加背景音乐和转场音效""" if not video_files: print("没有可用的视频文件进行拼接") return False total_files = len(video_files) print(f"开始拼接 {total_files} 个已带字幕的视频文件...") if progress_callback: progress_callback(0, f"准备拼接 {total_files} 个视频文件") # 加载所有视频片段 clips = [] try: for i, video_file in enumerate(video_files): progress = int((i / total_files) * 30) # 加载阶段占30%进度 if progress_callback: progress_callback(progress, f"加载视频片段 {i + 1}/{total_files}") print(f"加载视频片段 {i + 1}/{total_files}: {video_file}") try: clip = VideoFileClip(str(video_file)) clip = clip.set_fps(30) # 统一帧率 # 统一尺寸 if clips: target_size = clips[0].size if clip.size != target_size: print(f"警告: 视频 {video_file.name} 尺寸与其他不同,将调整为 {target_size}") clip = clip.resize(target_size) clips.append(clip) except Exception as e: print(f"加载视频失败 {video_file}: {e}") # 清理已加载的片段 for c in clips: safe_close(c) return False if not clips: print("没有成功加载任何视频片段") return False # 标准化音频电平 print("标准化音频电平...") if progress_callback: progress_callback(30, "正在标准化音频电平") clips = normalize_audio_levels(clips) # 使用更新后的目标音量 # 添加音频交叉淡入淡出 print("添加音频交叉淡入淡出...") clips = add_audio_crossfade(clips) # 添加转场音效 print("添加转场音效...") if progress_callback: progress_callback(32, "正在添加转场音效") clips_with_transitions = add_transition_effects(clips) print("拼接所有视频片段...") if progress_callback: progress_callback(35, "开始拼接视频片段") # 拼接所有片段 final_clip = concatenate_videoclips(clips_with_transitions, method="compose") # 处理背景音乐,可通过参数调整音量 if music_path: print(f"添加背景音乐: {Path(music_path).name}") if progress_callback: progress_callback(40, "正在处理背景音乐") # 在这里可以调整背景音乐音量,例如设置为0.1表示原音量的10% final_audio = process_background_music(final_clip, music_path, music_volume=0.07) if final_audio: final_clip = final_clip.set_audio(final_audio) else: print("使用原视频音频,未添加背景音乐") else: print("未指定背景音乐,使用原视频音频") if progress_callback: progress_callback(40, "未找到背景音乐,使用原视频音频") # 如果最终音频仍然很小,增加整体增益 if final_clip.audio: try: # 对于CompositeAudioClip,我们使用另一种方式计算音量 if isinstance(final_clip.audio, CompositeAudioClip): # 直接设置增益,略微提高整体音量 gain_factor = 1.5 # 增加整体音量 print(f"对复合音频应用固定增益: {gain_factor:.2f}倍") boosted_audio = final_clip.audio.volumex(gain_factor) final_clip = final_clip.set_audio(boosted_audio) else: # 检查音频的最大音量 max_volume = final_clip.audio.max_volume() print(f"最终音频最大音量: {max_volume}") # 如果音量小于0.5,增加增益 if max_volume < 0.5: gain_needed = 0.9 / max_volume # 从0.8提高到0.9,增加所需增益 if gain_needed > 3.5: # 从3提高到3.5,允许更大的增益 gain_needed = 3.5 print(f"增加音频增益: {gain_needed:.2f}倍") boosted_audio = final_clip.audio.volumex(gain_needed) final_clip = final_clip.set_audio(boosted_audio) except Exception as e: print(f"增加音频增益时出错: {e}") # 准备输出 output_path.parent.mkdir(parents=True, exist_ok=True) temp_output = output_path.with_suffix('.temp.mp4') # 备份已有文件 if output_path.exists(): backup_path = output_path.with_suffix(f'.backup_{int(time.time())}.mp4') shutil.move(str(output_path), str(backup_path)) print(f"已存在同名文件,已备份至: {backup_path}") print(f"正在渲染最终视频到: {output_path}") if progress_callback: progress_callback(50, "开始渲染最终视频") # 渲染进度回调 def render_progress(progress): render_percent = 50 + int(progress * 0.5) if progress_callback: progress_callback(render_percent, f"正在渲染: {int(progress * 100)}%") # 写入视频文件 final_clip.write_videofile( str(temp_output), codec="libx264", audio_codec="aac", fps=30, threads=1, preset="medium", # logger=None, ) temp_output.rename(output_path) print(f"视频拼接完成! 保存至: {output_path}") if progress_callback: progress_callback(100, "视频拼接完成") return True except Exception as e: print(f"视频渲染失败: {e}") if 'temp_output' in locals() and temp_output.exists(): temp_output.unlink() return False finally: # 确保所有资源都被释放 print("释放视频资源...") for clip in clips: safe_close(clip) if 'final_clip' in locals(): safe_close(final_clip) def generate_final_videos_by_subdir(progress_callback=None): """按子文件夹生成拼接视频,每个子文件夹生成一个视频文件并存储在对应子目录中""" config = file_utils.load_config() # 1. 获取视频文件所在的父目录 try: parent_dir = file_utils.ensure_directory_exists(config['paths']['subtitled_videos_dir']) except KeyError: parent_dir = file_utils.ensure_directory_exists(config['paths']['output_dir']) # 2. 获取所有直接子文件夹 subdirectories = get_subdirectories(parent_dir) if not subdirectories: print(f"在目录 {parent_dir} 下未找到任何子文件夹") return False print(f"找到 {len(subdirectories)} 个子文件夹,将逐个处理...") if progress_callback: progress_callback(0, f"找到 {len(subdirectories)} 个子文件夹") # 3. 获取背景音乐列表 background_music_list = get_background_music_list() if not background_music_list: print("警告: 未找到任何背景音乐文件,所有视频将使用原音频") else: print(f"已加载 {len(background_music_list)} 个背景音乐文件,将循环使用") # 4. 准备视频输出目录 try: video_dir = file_utils.ensure_directory_exists(config['paths']['video_dir']) except KeyError: print("警告: 配置中缺少video_dir,使用默认路径./data/video") video_dir = Path(__file__).parent.parent / "data" / "video" video_dir.mkdir(parents=True, exist_ok=True) # 5. 逐个处理子文件夹 all_success = True total_subdirs = len(subdirectories) for idx, subdir in enumerate(subdirectories, 1): subdir_name = subdir.name print(f"\n===== 开始处理子文件夹 ({idx}/{total_subdirs}): {subdir_name} =====") # 为当前子文件夹选择背景音乐(循环使用) current_music = None if background_music_list: music_index = (idx - 1) % len(background_music_list) # 循环索引 current_music = background_music_list[music_index] print(f"使用背景音乐: {Path(current_music).name} (第 {music_index + 1}/{len(background_music_list)} 个)") # 计算当前子文件夹处理在总进度中的占比 subdir_progress_base = int((idx - 1) / total_subdirs * 100) subdir_progress_range = int(100 / total_subdirs) def subdir_progress_callback(percent, message): """子文件夹处理的进度回调包装器""" overall_percent = subdir_progress_base + int(percent / 100 * subdir_progress_range) if progress_callback: progress_callback(overall_percent, f"子文件夹 {subdir_name}: {message}") # 获取当前子文件夹中的视频文件 video_files = get_subdirectory_videos(subdir) if not video_files: print(f"子文件夹 {subdir_name} 中没有可处理的视频文件,跳过...") all_success = False continue subdir_progress_callback(0, f"找到 {len(video_files)} 个视频文件") # 创建对应子目录 output_subdir = video_dir / subdir_name output_subdir.mkdir(parents=True, exist_ok=True) # 查找当前子目录中现有视频文件,确定起始编号 existing_files = list(output_subdir.glob("final_video_*.mp4")) if existing_files: # 提取现有文件的编号 numbers = [] for file in existing_files: match = re.search(r'final_video_(\d+)\.mp4', str(file)) if match: try: numbers.append(int(match.group(1))) except ValueError: continue current_number = max(numbers) + 1 if numbers else 1 else: current_number = 1 # 生成输出文件名(基于顺序编号) output_name = f"final_video_{current_number:03d}.mp4" # 使用3位数字编号,确保排序正确 output_path = output_subdir / output_name # 拼接当前子文件夹中的视频,传入选定的背景音乐 success = concatenate_videos(video_files, output_path, current_music, subdir_progress_callback) if success: # 计算总时长 total_duration = 0 for f in video_files: clip = None try: clip = VideoFileClip(str(f)) total_duration += clip.duration except Exception as e: print(f"计算视频 {f} 时长时出错: {e}") finally: safe_close(clip) # 保存视频信息(更新了音频相关参数) video_info = { "created_at": time.strftime("%Y-%m-%d %H:%M:%S"), "source_subdirectory": str(subdir), "video_count": len(video_files), "output_path": str(output_path), "total_duration": total_duration, "source_files": [str(f) for f in video_files], "background_music": current_music if current_music else "None", "background_music_name": Path(current_music).name if current_music else "None", "has_background_music": current_music is not None, "background_music_volume": 0.07, # 更新为当前使用的背景音乐音量 "has_transition_effects": len(get_all_transition_sounds()) > 0, "has_audio_crossfade": True, "audio_crossfade_duration": 0.1, "audio_boost_applied": True, "audio_boost_amount_db": 9.0, # 更新为当前使用的增益值 "audio_normalization_target": -14 # 更新为当前使用的标准化目标值 } info_path = output_path.with_suffix('.json') with open(info_path, 'w', encoding='utf-8') as f: json.dump(video_info, f, indent=2, ensure_ascii=False) print(f"子文件夹 {subdir_name} 视频信息保存至: {info_path}") else: print(f"子文件夹 {subdir_name} 视频拼接失败") all_success = False return all_success if __name__ == '__main__': print("开始按子文件夹拼接已带字幕的视频文件...") start_time = time.time() def print_progress(percent, message): """命令行进度显示回调函数""" print(f"进度: {percent}% - {message}") try: if generate_final_videos_by_subdir(print_progress): duration = time.time() - start_time print(f"\n所有子文件夹视频拼接完成! 总耗时: {duration:.2f}秒") else: print("\n部分或全部子文件夹视频拼接失败") except Exception as e: print(f"\n视频拼接过程中出错: {e}") import traceback traceback.print_exc() 加载背景音乐后,在应用的时候不按照顺序应用,采用随机的方式
最新发布
10-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值