def hls(self, fixed: bool = False, encrypt: bool = False) -> str: """ 生成m3u8格式的文件 :param fixed: 严格限制ts的时常 :param encrypt: 是否加密 :return: m3u8文件的所在的目录 """ fix_time = vargs.HLSTimeFixed.FIXED.value if fixed else vargs.HLSTimeFixed.NOFIXED.value encrypt_opts = utils_video._get_hls_enc_option() if encrypt else '' uuid_str = str(uuid.uuid1()) output_dir = f'/tmp/{uuid_str}/ts' self.tmpfiles = output_dir ts_prefix = uuid_str outpath = f'/tmp/{uuid_str}/{uuid_str}.m3u8' # if not utils_common._run_ffmpeg_cmd(f'mkdir -pv {output_dir}'): # return None try: cmd = ffcmd.CMD_HLS_VIDEO.format(inpath=self.filename, outpath=outpath, encrypt_opts=encrypt_opts, fix_time=fix_time, output_dir=output_dir, ts_prefix=ts_prefix) # print(cmd) if utils_common._run_ffmpeg_cmd(cmd): if utils_common._run_ffmpeg_cmd( f"sed -i s@/tmp/{uuid_str}/@@g {outpath}"): self.tmpfiles.remove(output_dir) return output_dir except ValueError: pass
def gif(self, start: float, last: float, height: int) -> str: """ 视频截图,从开始时间截取一帧作为图片 :param start: 开始时间 :param last: 持续时间,单位秒 :param height: 图片高度(宽度等比缩放) :return: 成功: 图片路径 | 失败: None """ try: if 0 < last <= self.duration and 0 <= start < self.duration - last \ and 0 < height <= vargs.VIDEO_HEIGHT: passlog_prefix = f"{tempfile.mktemp(dir='/tmp')}.png" self.tmpfiles = passlog_prefix # 加入临时文件集合,以便集中处理 outpath = f"{tempfile.mktemp(dir='/tmp')}.gif" # gif 输出文件 self.tmpfiles = outpath # 加入临时文件集合,以便集中处理 cmd = ffcmd.CMD_CREATEGIF.format(inpath=self.filename, outpath=outpath, start=start, last=last, height=height, passlog_prefix=passlog_prefix) if utils_common._run_ffmpeg_cmd(cmd): self.tmpfiles.remove(outpath) return outpath except ValueError: pass
def _init_video2mp4(inpath: str, outpath: str) -> bool: """ 将视频容器格式转化为mp4,并去除旋转元数据,统一视频流的index == 0, 音频流的index == 1 并且会去掉字幕流 :param inpath: 视频输入文件 :param outpath: 视频输出文件 :return: 成功: True| 失败: False """ return _run_ffmpeg_cmd( ffcmd.CMD_CH_VIDEO_META.format(inpath=inpath, outpath=outpath))
def move(self, newpath) -> bool: """ 将self.filename移动到newpath这个路径 newpath 不能是目录, newpath必须以.mp4结尾 :param newpath: self.filename 的新路径 :return: 成功: True | 失败: False """ newpath = os.path.abspath(newpath) if os.path.isdir(newpath): # 新路径是目录 return False if not newpath.endswith('.mp4'): # 新路径不是.mp4结尾 return False dir_path, file_path = os.path.split(newpath) if not utils_common._run_ffmpeg_cmd( f"mkdir -pv '{dir_path}'"): # 创建新路径的目录 return False if not utils_common._run_ffmpeg_cmd( f"mv -f '{self.filename}' '{newpath}'"): # 移动文件 return False self.filename = newpath return True
def _run_cutvideo_cmd(inpath: str, outpath: str, start: float, last: float) -> bool: """ 将视频从start处开始,裁剪到start+last处 :param inpath: 输入文件路径 :param outpath: 输出文件路径 :param start: 开始时间 :param last: 持续时间 :return: 命令执行成功: True | 失败: False """ cmd = ffcmd.CMD_CUTVIDEO.format(inpath=inpath, outpath=outpath, start=start, last=last) # print(cmd) return _run_ffmpeg_cmd(cmd)
def __add__(self, other: 'X264Codec'): """ 用于视频拼接 """ cls = self.__class__ if not isinstance(other, cls): raise ValueError(f'{other} is not a instance of {cls.__name__}.') concat_file = f"{tempfile.mktemp(dir='/tmp')}.txt" self.tmpfiles = concat_file concat_file_contend = f"file '{self.filename}'\nfile '{other.filename}'\n" utils_common._write_file(concat_file, concat_file_contend) outpath = f"{tempfile.mktemp(dir='/tmp')}.mp4" # 拼接后视频文件 self.tmpfiles = outpath # 加入临时文件集合,以便集中处理 cmd = ffcmd.CMD_CONCAT_VIDEO_SAFE.format(concat_file=concat_file, outpath=outpath) if utils_common._run_ffmpeg_cmd(cmd): self.tmpfiles.remove(outpath) return self.__class__.from_format_video(outpath) else: raise OSError('can not add this two X264Video.')
def snapshot(self, start: float, height: int) -> str: """ 视频截图,从开始时间截取一帧作为图片 :param start: 开始时间 :param height: 图片高度(宽度等比缩放) :return: 成功: 图片路径 | 失败: None """ try: if 0 <= start < self.duration and 0 < height <= vargs.VIDEO_HEIGHT: outpath = f"{tempfile.mktemp(dir='/tmp')}.jpg" # 截图输出文件 self.tmpfiles = outpath # 加入临时文件集合,以便集中处理 cmd = ffcmd.CMD_SNAPSHOT.format(inpath=self.filename, outpath=outpath, start=start, height=height) if utils_common._run_ffmpeg_cmd(cmd): self.tmpfiles.remove(outpath) return outpath except ValueError: pass
def _run_video2h264_cmd(inpath: str, inimgs: str, outpath: str, filter_opts: str, vbitrate: int) -> bool: """ 执行 ffcmd.CMD_VIDEO2H264 命令 :param inpath: 输入文件路径 :param inimgs: 输入图片路参数 :param outpath: 输出文件路径 :param filter_opts: 过滤器参数 :param vbitrate: 视频码率 :return: 命令执行成功: True | 失败: False """ passlog_prefix = f'/tmp/ffmpeg2passlog.{time.time()}.{os.path.split(tempfile.mktemp())[1]}' cmd = ffcmd.CMD_VIDEO2H264.format( inpath=inpath, inimgs=inimgs, outpath=outpath, passlog_prefix=passlog_prefix, filter_opts=filter_opts, vbitrate=vbitrate, ) # print(cmd) ret = _run_ffmpeg_cmd(cmd) # 执行 ffmpeg 命令 _run_cmd(f'rm -rf {passlog_prefix}*') # 删除pass日志 return ret