def __init__(self, filename): if(filename[0] != '/'): Log.w(TAG, "Filename is not absolute, this may cause issues dispatching jobs.") ffprobe = subprocess.Popen(["ffprobe","-v", "quiet", "-print_format", "json", "-show_format", "-show_streams",filename], stdout=subprocess.PIPE) #Get everything from stdout once ffprobe exits, and try: ffprobe_string = ffprobe.communicate()[0] self.ffprobe_dict=json.loads(ffprobe_string) except ValueError: Log.e(TAG, "File could not be read, are you sure it exists?") ffmpeg_interlace = subprocess.Popen(["ffmpeg", "-filter:v", "idet", "-frames:v", "400", "-an", "-f", "null", "-", "-i", filename],stdout=subprocess.PIPE, stderr=subprocess.PIPE) interlaced_details = ffmpeg_interlace.communicate()[1] interlaced_lines = interlaced_details.split("\n") num_progressive = 0 for line in interlaced_lines: if line.find("idet") != -1 and line.find("Progressive") != -1: #find the number of progressive frames in this line. nframes = line.split("Progressive:")[1].split("Undetermined")[0] num_progressive = num_progressive + int(nframes) if num_progressive < 20: self.is_interlaced = True self.video_stream = self.parse_video(self.ffprobe_dict) self.audio_streams = self.parse_audio(self.ffprobe_dict) self.sub_streams = self.parse_subs(self.ffprobe_dict) self.file_format = self.ffprobe_dict["format"]["format_name"] self.duration = float(self.ffprobe_dict["format"]["duration"])
def parse_video(self, ffprobe_dict): if ffprobe_dict == False: return foundVideo = False video_stream = {} for stream in ffprobe_dict["streams"]: if stream["codec_type"] == "video": if foundVideo: Log.w(TAG, "File had more than one video stream. Using the first one. This is unsupported!") foundVideo = True video_stream = {"index": stream["index"], "width": stream["width"], "height": stream["height"], "codec": stream["codec_name"] } return video_stream
def parse_video(self, ffprobe_dict): if ffprobe_dict == False: return foundVideo = False video_stream = {} for stream in ffprobe_dict["streams"]: if stream["codec_type"] == "video": if foundVideo: Log.w( TAG, "File had more than one video stream. Using the first one. This is unsupported!" ) foundVideo = True video_stream = { "index": stream["index"], "width": stream["width"], "height": stream["height"], "codec": stream["codec_name"] } return video_stream
def __init__(self, filename): if (filename[0] != '/'): Log.w( TAG, "Filename is not absolute, this may cause issues dispatching jobs." ) ffprobe = subprocess.Popen([ "ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", filename ], stdout=subprocess.PIPE) #Get everything from stdout once ffprobe exits, and try: ffprobe_string = ffprobe.communicate()[0] self.ffprobe_dict = json.loads(ffprobe_string) except ValueError: Log.e(TAG, "File could not be read, are you sure it exists?") ffmpeg_interlace = subprocess.Popen([ "ffmpeg", "-filter:v", "idet", "-frames:v", "400", "-an", "-f", "null", "-", "-i", filename ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) interlaced_details = ffmpeg_interlace.communicate()[1] interlaced_lines = interlaced_details.split("\n") num_progressive = 0 for line in interlaced_lines: if line.find("idet") != -1 and line.find("Progressive") != -1: #find the number of progressive frames in this line. nframes = line.split("Progressive:")[1].split( "Undetermined")[0] num_progressive = num_progressive + int(nframes) if num_progressive < 20: self.is_interlaced = True self.video_stream = self.parse_video(self.ffprobe_dict) self.audio_streams = self.parse_audio(self.ffprobe_dict) self.sub_streams = self.parse_subs(self.ffprobe_dict) self.file_format = self.ffprobe_dict["format"]["format_name"] self.duration = float(self.ffprobe_dict["format"]["duration"])
def media_transform(parser, options): #keep track if we do anything other than copying tcodeVideo = False tcodeAudio = False # Start with the video. # We're going to check the parser video_stream and compare it to our target. cstream = parser.video_stream voptions = options["video"] codec = "copy" if cstream["codec"] != voptions["codec"] or voptions["force"] == True: if voptions["allowhevc"] == True and cstream["codec"] == "hevc": Log.i(TAG, "Skipping transcode for HVEC as per override.") else: tcodeVideo = True Log.i(TAG, "Transcoding video track") codec = voptions["codec"] else: Log.i(TAG, "Copying video track") deinterlace = False if (parser.is_interlaced and voptions["deinterlace"] == "yes") or voptions["deinterlace"] == "forced": Log.i(TAG, "Deinterlacing video track (will cause transcode!)") tcodeVideo = True codec = voptions["codec"] deinterlace = True scaleopts = False if voptions["res"] != "keep": dres = 0 if voptions["res"] == "1080p": dres = 1080 elif voptions["res"] == "720p": dres = 720 elif voptions["res"] == "480p": dres = 480 if (cstream["height"] < dres): scaleopts = False elif (abs(cstream["height"] - dres) < 30): scaleopts = False else: Log.i(TAG, "Scaling video (will cause transcode!)") codec = voptions["codec"] scaleopts = dres bit10 = False if "10bit" in voptions: bit10 = voptions["10bit"] video_build = { "type": "video", "index": cstream["index"], "codec": codec, "quality": voptions["quality"], "deinterlacing": deinterlace, "scaleopts": scaleopts, "10bit": bit10 } if options["video"]["ignore"] == True: Log.w(TAG, "Ignoring incorrect video codec") video_build = { "type": "video", "index": cstream["index"], "codec": "copy", "quality": "10", "deinterlacing": False, "scaleopts": False } aoptions = options["audio"] audio_building = [] surround_exists = False stereo_exists = False #Now the hard part. Figuring out the mess of audio streams #Find the master track. This is the highest bitrate, highest number of channels stream, which is also in the right language. audio_master = {"channels": 0, 'language': 'und'} audio_stereo = None ignore_language = False valid_laguages = ["eng"] if "lang" in aoptions: if "ignore" in aoptions["lang"] and aoptions["lang"]["ignore"] == True: ignore_language = True if "allowed" in aoptions["lang"]: valid_languages = aoptions["lang"]["allowed"] #this feels naieve. Take a closer look at this! for track in parser.audio_streams: if ignore_language or (track["language"] in valid_languages or track["language"] == "und" or track["language"] == None): if track["channels"] > audio_master["channels"]: audio_master = track if track["channels"] < 6: audio_stereo = track stereo_exists = True if audio_master["channels"] > 2: surround_exists = True #Add our audio channels. #Use the existing surround track if surround_exists and aoptions["surround"]["keep"] == True: audio_building.append({ "type": "audio", "index": audio_master["index"], "codec": "copy", "ffprocdown": False, "downconvert": False }) Log.i(TAG, "Copying surround audio") #Use our existing stereo track. if stereo_exists and aoptions["stereo"]["keep"] == True: if "aac" == audio_stereo["codec"]: Log.i(TAG, "Copying stereo audio") audio_building.append({ "type": "audio", "index": audio_stereo["index"], "codec": "copy", "ffprocdown": False, "downconvert": False }) else: tcodeAudio = True Log.i(TAG, "Transcoding existing stereo audio") audio_building.append({ "type": "audio", "index": audio_stereo["index"], "codec": "aac", "bitrate": aoptions["stereo"]["bitrate"], "downconvert": False, "forcefdk": aoptions["stereo"]["force_libfdk"], "ffprocdown": False }) #Create from surround. if surround_exists and (not stereo_exists or aoptions["stereo"]["keep"] == False) and aoptions["stereo"]["create"] == True: Log.i(TAG, "Downmixing surround to stereo") tcodeAudio = True audio_building.append({ "type": "audio", "index": audio_master["index"], "codec": "aac", "bitrate": aoptions["stereo"]["bitrate"], "downconvert": True, "forcefdk": aoptions["stereo"]["force_libfdk"], "ffprocdown": aoptions["stereo"]["ffproc_filtering"] }) #Are we doing any transcoding? tcode = tcodeVideo or tcodeAudio remux = False if not tcode and parser.file_format.find( options["format"]["filetype"]) == -1: remux = True audio_building.append(video_build) return { "video": tcodeVideo, "audio": tcodeAudio, "remux": remux, "tcodeData": audio_building }
def media_transform(parser, options): #keep track if we do anything other than copying tcodeVideo=False tcodeAudio=False # Start with the video. # We're going to check the parser video_stream and compare it to our target. cstream = parser.video_stream voptions = options["video"] codec = "copy" if cstream["codec"] != voptions["codec"] or voptions["force"] == True: if voptions["allowhevc"] == True and cstream["codec"] == "hevc": Log.i(TAG, "Skipping transcode for HVEC as per override.") else: tcodeVideo = True Log.i(TAG, "Transcoding video track") codec = voptions["codec"] else: Log.i(TAG, "Copying video track") deinterlace = False if (parser.is_interlaced and voptions["deinterlace"] == "yes") or voptions["deinterlace"] == "forced": Log.i(TAG, "Deinterlacing video track (will cause transcode!)") tcodeVideo=True codec = voptions["codec"] deinterlace = True scaleopts = False if voptions["res"] != "keep": dres = 0 if voptions["res"] == "1080p": dres = 1080 elif voptions["res"] == "720p": dres = 720 elif voptions["res"] == "480p": dres = 480 if(cstream["height"] < dres): scaleopts = False elif(abs(cstream["height"] - dres) < 30): scaleopts = False else: Log.i(TAG, "Scaling video (will cause transcode!)") codec = voptions["codec"] scaleopts = dres video_build = {"type":"video", "index":cstream["index"], "codec": codec, "quality": voptions["quality"], "deinterlacing": deinterlace, "scaleopts": scaleopts} if options["video"]["ignore"] == True: Log.w(TAG, "Ignoring incorrect video codec") video_build = {"type":"video", "index":cstream["index"], "codec": "copy", "quality": "10", "deinterlacing": False, "scaleopts": False} aoptions = options["audio"] audio_building=[] surround_exists = False stereo_exists = False #Now the hard part. Figuring out the mess of audio streams #Find the master track. This is the highest bitrate, highest number of channels stream, which is also in the right language. audio_master = {"channels": 0, 'language':'und'} audio_stereo = None #this feels naieve. Take a closer look at this! for track in parser.audio_streams: if ( track["language"] == "eng" or track["language"] == "und" or track["language"] == None): if track["channels"] > audio_master["channels"]: audio_master = track if track["channels"] < 6: audio_stereo = track stereo_exists = True if audio_master["channels"] > 2: surround_exists = True #Add our audio channels. #Use the existing surround track if surround_exists and aoptions["surround"]["keep"] == True: audio_building.append({"type":"audio","index":audio_master["index"], "codec": "copy","ffprocdown":False,"downconvert":False}) Log.i(TAG, "Copying surround audio") #Use our existing stereo track. if stereo_exists and aoptions["stereo"]["keep"] == True: if "aac" == audio_stereo["codec"]: Log.i(TAG, "Copying stereo audio") audio_building.append({"type":"audio","index":audio_stereo["index"], "codec": "copy","ffprocdown":False,"downconvert":False}) else: tcodeAudio = True Log.i(TAG, "Transcoding existing stereo audio") audio_building.append({"type":"audio","index":audio_master["index"], "codec": "aac", "bitrate": aoptions["stereo"]["bitrate"],"downconvert":False, "forcefdk":aoptions["stereo"]["force_libfdk"],"ffprocdown":False}) #Create from surround. if surround_exists and (not stereo_exists or aoptions["stereo"]["keep"] == False) and aoptions["stereo"]["create"] == True: Log.i(TAG, "Downmixing surround to stereo") tcodeAudio = True audio_building.append({"type":"audio","index":audio_master["index"], "codec": "aac", "bitrate": aoptions["stereo"]["bitrate"],"downconvert":True, "forcefdk":aoptions["stereo"]["force_libfdk"],"ffprocdown":aoptions["stereo"]["ffproc_filtering"]}) #Are we doing any transcoding? tcode = tcodeVideo or tcodeAudio remux = False if not tcode and parser.file_format.find(options["format"]["filetype"]) == -1: remux = True audio_building.append(video_build) return {"video": tcodeVideo, "audio": tcodeAudio, "remux": remux, "tcodeData":audio_building}