CTA Test Content Generation Log (Generated at: '2020-06-15' '09:20:10') ----------------------------------- FFMPEG Information: ----------------------------------- ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04) configuration: --prefix=/usr --enable-gpl --enable-version3 --enable-nonfree --disable-static --enable-shared --disable-debug --enable-avresample --enable-libass --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxml2 --docdir=/usr/share/doc/ffmpeg-4.2.2 libavutil 56. 31.100 / 56. 31.100 libavcodec 58. 54.100 / 58. 54.100 libavformat 58. 29.100 / 58. 29.100 libavdevice 58. 8.100 / 58. 8.100 libavfilter 7. 57.100 / 7. 57.100 libavresample 4. 0. 0 / 4. 0. 0 libswscale 5. 5.100 / 5. 5.100 libswresample 3. 5.100 / 3. 5.100 libpostproc 55. 5.100 / 55. 5.100 ----------------------------------- Command run: ----------------------------------- /usr/bin/ffmpeg -i tos-30sec-final.mp4 -i tos-30sec-final.mp4 -map 0:v:0 -c:v:0 h264 -b:v:0 3000k -s:v:0 1920x1080 -r:v:0 60 -profile:v:0 high -color_primaries:v:0 1 -color_trc:v:0 1 -colorspace:v:0 1 -x264-params:v:0 level=40:no-open-gop=1:min-keyint=60:keyint=60:scenecut=0 -map 0:a:0 -c:a:0 aac -b:a:0 64k -adaptation_sets "id=0,streams=v id=1,streams=a" -format_options "movflags=cmaf" -seg_duration 2 -use_template 1 -use_timeline 0 -f dash output.mpd ----------------------------------- Encode.py: ----------------------------------- import sys, os, getopt from enum import Enum import subprocess from subprocess import PIPE from datetime import datetime class VideoCodecOptions(Enum): AVC = "h264" HEVC = "h265" class AudioCodecOptions(Enum): AAC = "aac" class AVCSD: m_profile = "high" m_level = "31" m_color_primary = "1" m_resolution_w = "864" m_resolution_h = "576" m_frame_rate = "60" class AVCHD: m_profile = "high" m_level = "40" m_color_primary = "1" m_resolution_w = "1920" m_resolution_h = "1080" m_frame_rate = "60" class AVCHDHF: m_profile = "high" m_level = "42" m_color_primary = "1" m_resolution_w = "1920" m_resolution_h = "1080" m_frame_rate = "60" class DASH: m_segment_duration = "2" m_segment_signaling = "template" def __init__(self, dash_config=None): if dash_config is not None: config = dash_config.split(',') for i in range(0, len(config)): config_opt = config[i].split(":") name = config_opt[0] value = config_opt[1] if name == "d": self.m_segment_duration = value elif name == "s": if value != "template" and value != "timeline": print("Segment Signaling can either be Segment Template denoted by \"template or " "SegmentTemplate with Segment Timeline denoted by \"timeline\".") exit(1) else: self.m_segment_signaling = value def dash_package_command(self, index_v, index_a): dash_command = "-adaptation_sets " if index_v > 0 and index_a>0: dash_command += "\"id=0,streams=v id=1,streams=a\" " elif index_v > 0 and index_a == 0: dash_command += "\"id=0,streams=v\" " elif index_v == 0 and index_a > 0: dash_command += "\"id=0,streams=a\" " else: print("At least one Represetation must be provided to be DASHed") sys.exit(1) dash_command += "-format_options \"movflags=cmaf\" " + \ "-seg_duration " + self.m_segment_duration + " " + \ "-use_template 1 " if self.m_segment_signaling is "timeline": dash_command += "-use_timeline 1 " else: dash_command += "-use_timeline 0 " dash_command += "-f dash" return dash_command class Representation: m_id = None m_input = None m_media_type = None m_codec = None m_cmaf_profile = None m_bitrate = None m_resolution_w = None m_resolution_h = None m_frame_rate = None m_aspect_ratio_x = None m_aspect_ratio_y = None m_profile = None m_level = None m_color_primary = None def __init__(self, representation_config): config = representation_config.split(",") for i in range(0, len(config)): config_opt = config[i].split(":") name = config_opt[0] value = config_opt[1] if name == "id": self.m_id = value elif name == "input": self.m_input = value elif name == "type": self.m_media_type = value elif name == "codec": if value != VideoCodecOptions.AVC.value and value != VideoCodecOptions.HEVC.value and value != AudioCodecOptions.AAC.value: print("Supported codecs are AVC denoted by \"h264\" and HEVC denoted by \"h265\" for video and" "AAC denoted by \"aac\".") sys.exit(1) self.m_codec = value elif name == "cmaf": self.m_cmaf_profile = value if value == "avcsd": if self.m_profile is None: self.m_profile = AVCSD.m_profile if self.m_level is None: self.m_level = AVCSD.m_level if self.m_frame_rate is None: self.m_frame_rate = AVCSD.m_frame_rate if self.m_color_primary is None: self.m_color_primary = AVCSD.m_color_primary if self.m_resolution_w is None and self.m_resolution_h is None: self.m_resolution_w = AVCSD.m_resolution_w self.m_resolution_h = AVCSD.m_resolution_h elif value == "avchd": if self.m_profile is None: self.m_profile = AVCHD.m_profile if self.m_level is None: self.m_level = AVCHD.m_level if self.m_frame_rate is None: self.m_frame_rate = AVCHD.m_frame_rate if self.m_color_primary is None: self.m_color_primary = AVCHD.m_color_primary if self.m_resolution_w is None and self.m_resolution_h is None: self.m_resolution_w = AVCHD.m_resolution_w self.m_resolution_h = AVCHD.m_resolution_h elif value == "avchdhf": if self.m_profile is None: self.m_profile = AVCHDHF.m_profile if self.m_level is None: self.m_level = AVCHDHF.m_level if self.m_frame_rate is None: self.m_frame_rate = AVCHDHF.m_frame_rate if self.m_color_primary is None: self.m_color_primary = AVCHDHF.m_color_primary if self.m_resolution_w is None and self.m_resolution_h is None: self.m_resolution_w = AVCHDHF.m_resolution_w self.m_resolution_h = AVCHDHF.m_resolution_h elif name == "bitrate": self.m_bitrate = value elif name == "res": resolution_w_h = value.split('x') self.m_resolution_w = resolution_w_h[0] self.m_resolution_h = resolution_w_h[1] elif name == "fps": self.m_frame_rate = value elif name == "sar": sar_x_y = value.split(':') self.m_aspect_ratio_x = sar_x_y[0] self.m_aspect_ratio_y = sar_x_y[1] elif name == "profile": self.m_profile = value elif name == "level": self.m_level = value elif name == "color": self.m_color_primary = value else: print("Unknown configuration option for representation: " + name + " , it will be ignored.") if self.m_id is None or self.m_input is None or self.m_media_type is None or self.m_codec is None or \ self.m_bitrate is None or self.m_cmaf_profile is None: print("For each representation at least the following 6 parameters must be provided: " + ",,,,,") sys.exit(1) def form_command(self, index): input_file_command = "-i " + self.m_input command = "" if self.m_media_type in ("v", "video"): command += "-map " + index + ":v:0" + " " \ "-c:v:" + index + " " + self.m_codec + " " + \ "-b:v:" + index + " " + self.m_bitrate + "k " + \ "-s:v:" + index + " " + self.m_resolution_w + "x" + self.m_resolution_h + " " + \ "-r:v:" + index + " " + self.m_frame_rate + " " + \ "-profile:v:" + index + " " + self.m_profile + " " + \ "-color_primaries:v:" + index + " " + self.m_color_primary + " " + \ "-color_trc:v:" + index + " " + self.m_color_primary + " " + \ "-colorspace:v:" + index + " " + self.m_color_primary + " " if self.m_aspect_ratio_x is not None and self.m_aspect_ratio_y is not None: command += "-vf:v:" + index + " " + "\"setsar=" + self.m_aspect_ratio_x + "/" + self.m_aspect_ratio_y + "\" " if self.m_codec == VideoCodecOptions.AVC.value: command += "-x264-params:v:" + index + " " elif self.m_codec == VideoCodecOptions.HEVC.value: command += "-x265-params:v:" + index + " " command += "level=" + self.m_level + ":" \ "no-open-gop=1" + ":" \ "min-keyint=" + self.m_frame_rate + ":" \ "keyint=" + self.m_frame_rate + ":" \ "scenecut=0" elif self.m_media_type in ("a", "audio"): command += "-map " + index + ":a:0" + " " \ "-c:a:" + index + " " + self.m_codec + " " + \ "-b:a:" + index + " " + self.m_bitrate + "k" return [input_file_command, command] def generate_log(ffmpeg_path, command): now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") date = now.split(" ")[0] time = now.split(" ")[1] result = subprocess.run(ffmpeg_path + " -version", shell=True, stdout=PIPE, stderr=PIPE) script = "" with open('encode.py', 'r') as file: script = file.read() filename = "CTATestContentGeneration_Log_" + date + "_" + time f = open(filename, "w+") f.write("CTA Test Content Generation Log (Generated at: " + "'{0}' '{1}'".format(date, time) + ")\n\n\n\n") f.write("-----------------------------------\n") f.write("FFMPEG Information:\n") f.write("-----------------------------------\n") f.write("%s\n\n\n\n" % result.stdout.decode('ascii')) f.write("-----------------------------------\n") f.write("Command run:\n") f.write("-----------------------------------\n") f.write("%s\n\n\n\n" % command) f.write("-----------------------------------\n") f.write("Encode.py:\n") f.write("-----------------------------------\n") f.write("%s\n\n\n\n" % script) f.close() def parse_args(args): ffmpeg_path = None output_file = None representations = None dashing = None for opt, arg in args: if opt == '-h': print('test.py -i -o ') sys.exit() elif opt in ("-p", "--path"): ffmpeg_path = arg elif opt in ("-o", "--out"): output_file = arg elif opt in ("-r", "--reps"): representations = arg.split(" ") elif opt in ("-d", "--dash"): dashing = arg return [ffmpeg_path, output_file, representations, dashing] def assert_configuration(configuration): ffmpeg_path = configuration[0] output_file = configuration[1] representations = configuration[2] dashing = configuration[3] result = subprocess.run(ffmpeg_path + " -version", shell=True, stdout=PIPE, stderr=PIPE) if "ffmpeg version" not in result.stdout.decode('ascii'): print("FFMPEG binary is checked in the \"" + ffmpeg_path + "\" path, but not found.") sys.exit(1) if output_file is None: print("Output file name must be provided") sys.exit(1) if representations is None: print("Representations description must be provided.") sys.exit(1) if dashing is None: print("Warning: DASHing information is not provided, as a default setting, segment duration of 2 seconds and " "segment signaling of SegmentTemplate will be used.") if __name__ == "__main__": # Read input and parse try: arguments, values = getopt.getopt(sys.argv[1:], 'ho:r:d:p', ['out=', 'reps=', 'dash=', 'path=']) except getopt.GetoptError: sys.exit(2) configuration = parse_args(arguments) assert_configuration(configuration) ffmpeg_path = configuration[0] output_file = configuration[1] representations = configuration[2] dash = configuration[3] # Prepare the encoding for each Representation options = [] index_v = 0 index_a = 0 for representation_config in representations: representation = Representation(representation_config) if representation.m_media_type in ("v", "video"): options.append(representation.form_command(str(index_v))) index_v += 1 elif representation.m_media_type in ("a", "audio"): options.append(representation.form_command(str(index_a))) index_a += 1 else: print("Media type for a representation denoted by can either be \"v\" or \"video\" fro video media" "or \"a\" or \"audio\" for audio media.") exit(1) input_command = "" encode_command = "" for i in range(0,len(options)): option_i = options[i] input_command += option_i[0] + " " encode_command += option_i[1] + " " # Prepare the DASH formatting dash_options = DASH(dash) dash_package_command = dash_options.dash_package_command(index_v, index_a) command = ffmpeg_path + " " + \ input_command + " " + \ encode_command + " " + \ dash_package_command + " " + \ output_file subprocess.run(command, shell=True) generate_log(ffmpeg_path, command)