コード例 #1
0
def convert_subtitles_to_srt(i: str, o: str):
    ext = os.path.splitext(i)[1]
    if ext == '.srt':
        import shutil
        shutil.copy(i, o)
    elif ext in ('.ttml', '.xml', '.dfxp', '.tt'):
        # TTML
        from media_management_scripts.support.ttml2srt import convert_to_srt
        convert_to_srt(i, o)
    else:
        # VTT, SCC, etc

        from pycaption import detect_format, SRTWriter
        subtitle_str = _read_file(i)
        reader = detect_format(subtitle_str)
        if reader:
            subtitle_str = SRTWriter().write(reader().read(subtitle_str))
            with open(o, 'w') as file:
                file.write(subtitle_str)
        else:
            # Attempt to use FFMPEG
            from media_management_scripts.support.executables import ffmpeg
            from media_management_scripts.support.executables import execute_with_output
            args = [
                ffmpeg(), '-loglevel', 'fatal', '-y', '-i', i, '-c:s', 'srt', o
            ]
            ret, output = execute_with_output(args)
            if ret != 0:
                raise Exception(
                    'Exception during subtitle conversion: {}'.format(output))
コード例 #2
0
def _execute_ffmpeg(input_file: str, frames: int, start: int = 0):
    args = [
        ffmpeg(), '-ss',
        str(start), '-i', input_file, '-filter:v', 'idet', '-frames:v',
        str(frames), '-an', '-f', 'rawvideo', '-y', '/dev/null'
    ]
    ret, output = execute_with_output(args, print_output=False)
    if ret != 0:
        raise Exception('Non-zero ffmpeg return code: {}. Output={}'.format(
            ret, output))
    return _parse_output(output)
コード例 #3
0
def _temp_convert(input, output, print_output=False):
    # ffmpeg -i input1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
    print('Converting {}'.format(input))
    args = [
        ffmpeg(), '-loglevel', 'fatal', '-i', input, '-c:v', 'copy', '-c:a',
        'copy', '-c:s', 'copy', '-bsf:v', 'h264_mp4toannexb', '-f', 'mpegts',
        output
    ]
    ret, r = execute_with_output(args, print_output=print_output)
    if ret != 0:
        raise Exception('Error during ffmpeg: {}'.format(r))
コード例 #4
0
def _concat(files, output, print_output=False):
    # ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc output.mp4
    concat_str = 'concat:'
    for f in files:
        concat_str += f + '|'
    concat_str = concat_str[0:-1]
    print(concat_str)
    args = [
        ffmpeg(), '-loglevel', 'fatal', '-y', '-i', concat_str, '-c:v', 'copy',
        '-c:a', 'copy', '-c:s', 'copy', '-bsf:a', 'aac_adtstoasc', output
    ]
    ret, r = execute_with_output(args, print_output=print_output)
    if ret != 0:
        raise Exception('Error during ffmpeg: {}'.format(r))
コード例 #5
0
def cut(input, output, start=None, end=None):
    if check_exists(output):
        return -1
    create_dirs(output)
    args = [ffmpeg(), '-i', input]
    args.extend(['-c:v', 'copy'])
    args.extend(['-c:a', 'copy'])
    args.extend(['-c:s', 'copy'])
    args.extend(['-map', '0'])
    if start:
        args.extend(['-ss', str(start)])
    if end:
        args.extend(['-to', str(end)])
    args.append(output)
    return execute(args)
コード例 #6
0
def combine(video,
            srt,
            output,
            lang=None,
            overwrite=False,
            convert=False,
            crf=DEFAULT_CRF,
            preset=DEFAULT_PRESET,
            skip_eia_608=True):
    if not overwrite and check_exists(output):
        return -1

    if srt.endswith('.ttml') or srt.endswith('.xml'):
        logger.debug('Converting ttml/xml to srt')
        name, _ = os.path.splitext(srt)
        srt_out = name + '.srt'
        convert_to_srt(srt, srt_out)
        srt = srt_out
    create_dirs(output)
    args = [ffmpeg(), '-i', video]
    if overwrite:
        args.append('-y')
    args.extend(['-i', srt])
    args.extend(['-map', '0:v', '-map', '0:a'])
    if skip_eia_608:
        metadata = extract_metadata(video)
        for i in (s.index for s in metadata.subtitle_streams
                  if s.codec != 'eia_608'):
            args.extend(['-map', '0:{}'.format(i)])
    else:
        args.extend(['-map', '0:s?'])
    args.extend(['-map', '1:0'])
    if convert:
        args.extend(['-c:v', 'libx264', '-crf', str(crf), '-preset', preset])
        args.extend(['-c:a', 'aac'])
    else:
        args.extend(['-c:v', 'copy'])
        args.extend(['-c:a', 'copy'])
    args.extend(['-c:s', 'copy'])
    # -metadata:s:s:0 language=eng
    if lang:
        args.extend(['-metadata:s:s:0', 'language=' + lang])
    args.append(output)
    return execute(args)
コード例 #7
0
    def convert(self, in_file, out_file, commercials, metadata):
        invert = invert_commercial(commercials)
        temp_files = []
        wo_ext = os.path.basename(in_file).replace('.wtv', '')
        try:
            args = [ffmpeg(), '-i', in_file]
            for i in invert:
                temp_file = os.path.join(
                    self.temp_dir, wo_ext + '.' + str(len(temp_files)) + '.ts')
                temp_files.append(temp_file)
                if os.path.isfile(temp_file):
                    os.remove(temp_file)
                args.extend(self.cut_args(i, temp_file))

            ret, output = execute_with_output(args)
            if ret != 0:
                logger.error('Nonzero return code from ffmpeg: {}'.format(ret))
                return False
            else:
                input = 'concat:{}'.format('|'.join(temp_files))
                ret = convert_with_config(input,
                                          out_file,
                                          config=self.convert_config,
                                          print_output=False,
                                          overwrite=True,
                                          metadata=metadata)
                if ret != 0:
                    logger.error(
                        'Nonzero return code from ffmpeg: {}'.format(ret))
                # Cleanup temp files
                if not self.debug:
                    for f in temp_files:
                        os.remove(f)
        except Exception as e:
            logger.exception('Exception')
            return False
        return True
コード例 #8
0
def create_remux_args(input_files: List[str],
                      output_file: str,
                      mappings: List,
                      overwrite=False,
                      metadata={}):
    """

    :param input_files:
    :param output_file:
    :param mappings:
    :param overwrite:
    :param metadata: {'s:0': {'language':'eng'}, '': {'title': 'A Great Movie'}}
    :return:
    """
    if not overwrite and check_exists(output_file):
        return -1
    args = [ffmpeg()]
    if overwrite:
        args.append('-y')
    for input_file in input_files:
        args.extend(['-i', input_file])
    args.extend(['-c', 'copy'])
    for m in mappings:
        if type(m) == int:
            args.extend(['-map', '0:{}'.format(m)])
        else:
            args.extend(['-map', m])
    for key, value in metadata.items():
        if key:
            key = ':s:' + key
        for meta_key, meta_val in value.items():
            args.append('-metadata{}'.format(key))
            args.append('{}={}'.format(meta_key, meta_val))

    args.append(output_file)
    return args
コード例 #9
0
def create_test_video(
        length: int = 30,
        video_def: VideoDefinition = VideoDefinition(),
        audio_defs: List[AudioDefition] = [AudioDefition()],
        output_file=None,
        metadata: Dict[str, str] = None) -> _TemporaryFileWrapper:
    """
    Creates a video file matching the given video & audio definitions. If an output_file is not provided, a NamedTemporaryFile is used and returned
    :param length: the length of the file in seconds (Note, depending on codecs, the exact length may vary slightly)
    :param video_def: the video definition (codec, resolution, etc)
    :param audio_defs: the list of audio tracks
    :param output_file: the output file (or None for a NamedTemporaryFile)
    :return: the NamedTemporaryFile if no output_file was provided
    """
    audio_files = []
    if len(audio_defs) > 0:
        with NamedTemporaryFile(suffix='.wav') as raw_audio_file:
            # Create raw audio
            args = [
                ffmpeg(), '-y', '-ar', '48000', '-f', 's16le', '-i',
                '/dev/urandom', '-t',
                str(length), raw_audio_file.name
            ]
            _execute(args)
            for audio_def in audio_defs:
                audio_file = NamedTemporaryFile(
                    suffix='.{}'.format(audio_def.codec.extension))
                audio_files.append(audio_file)
                args = [
                    ffmpeg(), '-y', '-i', raw_audio_file.name, '-strict', '-2',
                    '-c:a', audio_def.codec.ffmpeg_codec_name, '-ac',
                    str(audio_def.channels.num_channels), '-t',
                    str(length), audio_file.name
                ]
                _execute(args)

    # ffmpeg -f lavfi -i testsrc=duration=10:size=1280x720:rate=30 testsrc.mpg
    if not output_file:
        file = NamedTemporaryFile(
            suffix='.{}'.format(video_def.container.extension))
    args = [
        ffmpeg(), '-y', '-f', 'lavfi', '-i',
        'testsrc=duration={}:size={}x{}:rate=30'.format(
            length, video_def.resolution.width, video_def.resolution.height)
    ]

    for f in audio_files:
        args.extend(['-i', f.name])
    args.extend(['-c:v', video_def.codec.ffmpeg_encoder_name])

    if video_def.interlaced:
        args.extend(['-vf', 'tinterlace=6'])

    if len(audio_defs) > 0:
        args.extend(['-c:a', 'copy'])

    for i in range(len(audio_defs) + 1):
        args.extend(['-map', str(i)])

    if metadata:
        for key, value in metadata.items():
            args.extend(['-metadata', '{}={}'.format(key, value)])

    if output_file:
        args.extend(['-t', str(length), output_file])
    else:
        args.extend(['-t', str(length), file.name])

    _execute(args)
    for f in audio_files:
        f.close()
    if not output_file:
        return file
コード例 #10
0
def convert_with_config(input,
                        output,
                        config: ConvertConfig,
                        print_output=True,
                        overwrite=False,
                        metadata=None,
                        mappings=None,
                        use_nice=True):
    """

    :param input:
    :param output:
    :param config:
    :param print_output:
    :param overwrite:
    :param metadata:
    :param mappings: List of mappings (for example ['0:0', '0:1'])
    :return:
    """
    if not overwrite and check_exists(output):
        return -1
    if print_output:
        print('Converting {} -> {}'.format(input, output))
        print('Using config: {}'.format(config))

    if not metadata:
        metadata = create_metadata_extractor().extract(
            input, detect_interlace=config.deinterlace)
    elif config.deinterlace and not metadata.interlace_report:
        raise Exception(
            'Metadata provided without interlace report, but convert requires deinterlace checks'
        )

    if metadata.resolution not in (Resolution.LOW_DEF, Resolution.STANDARD_DEF,
                                   Resolution.MEDIUM_DEF,
                                   Resolution.HIGH_DEF) and not config.scale:
        print('{}: Resolution not supported for conversion: {}'.format(
            input, metadata.resolution))
        # TODO Handle converting 4k content in H.265/HVEC
        return -2
    if use_nice and nice_exe:
        args = [nice_exe, ffmpeg()]
    else:
        args = [ffmpeg()]
    if overwrite:
        args.append('-y')
    args.extend(['-i', input])

    if config.scale:
        args.extend(['-vf', 'scale=-1:{}'.format(config.scale)])

    args.extend(['-c:v', config.video_codec])
    crf = config.crf
    bitrate = config.bitrate
    if VideoCodec.H264.equals(
            config.video_codec
    ) and config.bitrate is not None and config.bitrate != 'disabled':
        crf = 1
        # -x264-params vbv-maxrate=1666:vbv-bufsize=3332:crf-max=22:qpmax=34
        if config.bitrate == 'auto':
            bitrate = auto_bitrate_from_config(metadata.resolution, config)
        params = 'vbv-maxrate={}:vbv-bufsize={}:crf-max=25:qpmax=34'.format(
            str(bitrate), str(bitrate * 2))
        args.extend(['-x264-params', params])
    elif VideoCodec.H265.equals(
            config.video_codec
    ) and config.bitrate is not None and config.bitrate != 'disabled':
        raise Exception('Avg Bitrate not supported for H265')

    args.extend(['-crf', str(crf), '-preset', config.preset])
    if config.deinterlace:
        is_interlaced = metadata.interlace_report.is_interlaced(
            config.deinterlace_threshold)
        if print_output:
            print('{} - Interlaced: {}'.format(metadata.interlace_report,
                                               is_interlaced))
        if is_interlaced:
            # Video is interlaced, so add the deinterlace filter
            args.extend(['-vf', 'yadif'])

    args.extend(['-c:a', config.audio_codec])

    index = 0
    for audio in metadata.audio_streams:
        if audio.channels == 7:
            # 6.1 sound, so mix it up to 7.1
            args.extend(['-ac:a:{}'.format(index), '8'])
        index += 1

    if config.include_subtitles:
        args.extend(['-c:s', 'copy'])

    if not mappings:
        args.extend(['-map', '0'])
    else:
        for m in mappings:
            if type(m) == int:
                args.extend(['-map', '0:{}'.format(m)])
            else:
                args.extend(['-map', m])

    if config.include_meta:
        args.extend(['-metadata', 'ripped=true'])
        args.extend(['-metadata:s:v:0', 'ripped=true'])
    args.append(output)

    return execute(args, print_output)