Better performances and simplification by removing pipes and using memory file descriptors.

This commit is contained in:
Frédéric Tronel
2023-12-02 17:25:55 +01:00
parent d549311e20
commit 076e3c990b

View File

@@ -8,7 +8,8 @@ from datetime import datetime,timedelta,time
import coloredlogs, logging import coloredlogs, logging
from functools import cmp_to_key from functools import cmp_to_key
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import os from os import read, write, lseek, pipe, set_inheritable, memfd_create, SEEK_SET, close, unlink
import os.path
from io import BytesIO, TextIOWrapper from io import BytesIO, TextIOWrapper
import json import json
from enum import Enum, IntEnum, unique, auto from enum import Enum, IntEnum, unique, auto
@@ -63,7 +64,7 @@ def getFormat(inputFile):
infd = inputFile.fileno() infd = inputFile.fileno()
inputFile.seek(0,0) inputFile.seek(0,0)
os.set_inheritable(infd, True) set_inheritable(infd, True)
with Popen(['ffprobe', '-loglevel', 'quiet', '-show_format', '-of', 'json', '-i', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: with Popen(['ffprobe', '-loglevel', 'quiet', '-show_format', '-of', 'json', '-i', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe:
out, _ = ffprobe.communicate() out, _ = ffprobe.communicate()
out = json.load(BytesIO(out)) out = json.load(BytesIO(out))
@@ -79,7 +80,7 @@ def getStreams(inputFile):
infd = inputFile.fileno() infd = inputFile.fileno()
inputFile.seek(0,0) inputFile.seek(0,0)
os.set_inheritable(infd, True) set_inheritable(infd, True)
with Popen(['ffprobe', '-loglevel', 'quiet', '-show_streams', '-of', 'json', '-i', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: with Popen(['ffprobe', '-loglevel', 'quiet', '-show_streams', '-of', 'json', '-i', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe:
out, _ = ffprobe.communicate() out, _ = ffprobe.communicate()
out = json.load(BytesIO(out)) out = json.load(BytesIO(out))
@@ -220,8 +221,8 @@ def ffmpegConvert(inputFile, inputFormat, outputFile, outputFormat, duration):
infd = inputFile.fileno() infd = inputFile.fileno()
outfd = outputFile.fileno() outfd = outputFile.fileno()
os.set_inheritable(infd, True) set_inheritable(infd, True)
os.set_inheritable(outfd, True) set_inheritable(outfd, True)
# TODO: canvas size to be fixed ! # TODO: canvas size to be fixed !
with Popen(['ffmpeg', '-y', '-loglevel', 'quiet', '-progress', '/dev/stdout', '-canvas_size', '720x560', '-f', inputFormat, '-i', '/proc/self/fd/%d' % infd, with Popen(['ffmpeg', '-y', '-loglevel', 'quiet', '-progress', '/dev/stdout', '-canvas_size', '720x560', '-f', inputFormat, '-i', '/proc/self/fd/%d' % infd,
'-map', '0:v', '-map', '0:a', '-map', '0:s', '-bsf:v', 'h264_mp4toannexb,dump_extra=freq=keyframe', '-vcodec', 'copy', '-acodec', 'copy', '-scodec', 'dvdsub', '-map', '0:v', '-map', '0:a', '-map', '0:s', '-bsf:v', 'h264_mp4toannexb,dump_extra=freq=keyframe', '-vcodec', 'copy', '-acodec', 'copy', '-scodec', 'dvdsub',
@@ -241,7 +242,7 @@ def ffmpegConvert(inputFile, inputFormat, outputFile, outputFormat, duration):
def getFramesInStream(inputFile, begin, end, streamKind, subStreamId=0): def getFramesInStream(inputFile, begin, end, streamKind, subStreamId=0):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
infd = inputFile.fileno() infd = inputFile.fileno()
os.set_inheritable(infd, True) set_inheritable(infd, True)
with Popen(['ffprobe', '-loglevel', 'quiet', '-read_intervals', ('%s%%%s' %(begin, end)), '-show_entries', 'frame', '-select_streams', '%s:%d' % (streamKind, subStreamId), '-of', 'json', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: with Popen(['ffprobe', '-loglevel', 'quiet', '-read_intervals', ('%s%%%s' %(begin, end)), '-show_entries', 'frame', '-select_streams', '%s:%d' % (streamKind, subStreamId), '-of', 'json', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe:
out, _ = ffprobe.communicate() out, _ = ffprobe.communicate()
@@ -272,7 +273,7 @@ def getNearestIFrame(inputFile, timestamp, before=True, delta=timedelta(seconds=
tbegin = zero tbegin = zero
infd = inputFile.fileno() infd = inputFile.fileno()
os.set_inheritable(infd, True) set_inheritable(infd, True)
logger.debug('Looking for iframe in [%s, %s]' % (tbegin, tend)) logger.debug('Looking for iframe in [%s, %s]' % (tbegin, tend))
@@ -324,8 +325,8 @@ def extractMKVPart(inputFile, outputFile, begin, end):
outputFile.seek(0,0) outputFile.seek(0,0)
infd = inputFile.fileno() infd = inputFile.fileno()
outfd = outputFile.fileno() outfd = outputFile.fileno()
os.set_inheritable(infd, True) set_inheritable(infd, True)
os.set_inheritable(outfd, True) set_inheritable(outfd, True)
warnings = [] warnings = []
with Popen(['mkvmerge', '-o', '/proc/self/fd/%d' % outfd, '--split', 'parts:%s-%s' % (begin, end), '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as mkvmerge: with Popen(['mkvmerge', '-o', '/proc/self/fd/%d' % outfd, '--split', 'parts:%s-%s' % (begin, end), '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as mkvmerge:
pb = tqdm(TextIOWrapper(mkvmerge.stdout, encoding="utf-8"), total=100, unit='%', desc='Extraction') pb = tqdm(TextIOWrapper(mkvmerge.stdout, encoding="utf-8"), total=100, unit='%', desc='Extraction')
@@ -354,10 +355,9 @@ def extractPictures(inputFile, begin, nbFrames, width=640, height=480):
inputFile.seek(0,0) inputFile.seek(0,0)
infd = inputFile.fileno() infd = inputFile.fileno()
fdr, fdw = os.pipe() outfd = memfd_create('pictures', flags=0)
os.set_inheritable(infd, True) set_inheritable(outfd, True)
os.set_inheritable(fdr, False) # PPM header
os.set_inheritable(fdw, True)
# "P6\nWIDTH HEIGHT\n255\n" # "P6\nWIDTH HEIGHT\n255\n"
headerLen=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1 headerLen=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1
logger.debug('Header length: %d' % headerLen) logger.debug('Header length: %d' % headerLen)
@@ -365,67 +365,48 @@ def extractPictures(inputFile, begin, nbFrames, width=640, height=480):
length = imageLength*nbFrames length = imageLength*nbFrames
logger.debug("Estimated length: %d" % length) logger.debug("Estimated length: %d" % length)
pg = trange(nbFrames)
images = bytes() images = bytes()
with Popen(['ffmpeg', '-loglevel', 'quiet' ,'-y', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % infd, '-s', '%dx%d'%(width, height), '-vframes', '%d'%nbFrames, '-c:v', 'ppm', '-f', 'image2pipe', '/proc/self/fd/%d' % fdw ], stdout=PIPE, close_fds=False) as ffmpeg: with Popen(['ffmpeg', '-loglevel', 'quiet' ,'-y', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % infd, '-s', '%dx%d'%(width, height), '-vframes', '%d'%nbFrames, '-c:v', 'ppm', '-f', 'image2pipe', '/proc/self/fd/%d' % outfd ], stdout=PIPE, close_fds=False) as ffmpeg:
while ffmpeg.poll() == None: status = ffmpeg.wait()
# TODO: understand why this line ends up in reading on an already closed file descriptor if status != 0:
# fds, _, _ = select([fdr, ffmpeg.stdout], [], [], .1) logger.error('Conversion failed with status code: %d' % status)
fds, _, _ = select([fdr], [], [], .1) return None, None
if fdr in fds:
buf = os.read(fdr, imageLength)
# print("Read %d bytes of image. ffmpeg finished: %s" % (len(buf), ffmpeg.poll()))
if len(buf) == 0:
break
pg.update(len(buf)/imageLength)
images=images+buf
if ffmpeg.stdout in fds:
for line in TextIOWrapper(ffmpeg.stdout, encoding="utf-8"):
logger.debug(line)
status = ffmpeg.wait()
# Finishing to read residual bytes from pipe
while True:
fd, _, _ = select([fdr], [], [], .1)
if fd != []:
buf = os.read(fdr, imageLength)
# print("Read %d bytes of image" % len(buf))
if len(buf) == 0:
break
pg.update(len(buf)/imageLength)
images=images+buf
else:
# Nothing more to read
break
logger.debug("%d bytes received." % len(images))
os.close(fdr)
os.close(fdw)
if status != 0:
logger.error('Image extraction returns error code: %d' % status)
return images
def extractSound(inputFile, begin, outputFile, subChannel=0, nbPackets=0, sampleRate=48000, nbChannels=2): lseek(outfd, 0, SEEK_SET)
images = read(outfd,length)
if len(images) != length:
logger.info("Received %d bytes but %d were expected." % (len(images), length))
return None, None
lseek(outfd, 0, SEEK_SET)
return images, outfd
def extractSound(inputFile, begin, outputFileName, packetDuration, subChannel=0, nbPackets=0, sampleRate=48000, nbChannels=2):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
inputFile.seek(0,0) inputFile.seek(0,0)
outputFile.seek(0,0) outfd = memfd_create(outputFileName, flags=0)
infd = inputFile.fileno() infd = inputFile.fileno()
outfd = outputFile.fileno() set_inheritable(infd, True)
os.set_inheritable(infd, True) set_inheritable(outfd, True)
os.set_inheritable(outfd, True) sound = bytes()
with Popen(['ffmpeg', '-y', '-loglevel', 'quiet', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % infd, '-frames:a:%d' % subChannel, '%d' % nbPackets, length = int(nbChannels*sampleRate*4*nbPackets*packetDuration/1000)
'-c:a', 'pcm_s32le', '-sample_rate', '%d' % sampleRate, '-channels', '%d' % nbChannels, '-f', 's32le', '/proc/self/fd/%d' % outfd], stdout=PIPE, close_fds=False) as ffmpeg:
for line in TextIOWrapper(ffmpeg.stdout, encoding="utf-8"):
logger.debug(line)
status = ffmpeg.wait() with Popen(['ffmpeg', '-y', '-loglevel', 'quiet', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % infd, '-frames:a:%d' % subChannel, '%d' % (nbPackets+1),
if status != 0: '-c:a', 'pcm_s32le', '-sample_rate', '%d' % sampleRate, '-channels', '%d' % nbChannels, '-f', 's32le', '/proc/self/fd/%d' % outfd], stdout=PIPE, close_fds=False) as ffmpeg:
status = ffmpeg.wait()
if status != 0:
logger.error('Sound extraction returns error code: %d' % status) logger.error('Sound extraction returns error code: %d' % status)
return None, None
lseek(outfd, 0, SEEK_SET)
sound = read(outfd, length)
if (len(sound) != length):
logger.info("Received %d bytes but %d were expected (channels=%d, freq=%d, packets=%d, duration=%d ms)." % (len(sound), length, nbChannels, sampleRate, nbPackets, packetDuration))
return None, None
return sound, outfd
def dumpPPM(pictures, prefix, temporaries): def dumpPPM(pictures, prefix, temporaries):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -463,14 +444,12 @@ def dumpPPM(pictures, prefix, temporaries):
length=headerLen+3*width*height length=headerLen+3*width*height
nbBytes = 0 nbBytes = 0
while nbBytes < length: while nbBytes < length:
nbBytes+=os.write(outfd, pictures[pos+nbBytes:pos+length]) nbBytes+=write(outfd, pictures[pos+nbBytes:pos+length])
pos+=length pos+=length
picture+=1 picture+=1
def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, width, height, temporaries, dumpMemFD=False):
def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, width, height, temporaries, dumpPictures=False):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# encoderParams = [ 'ffmpeg', '-y', '-loglevel', 'quiet' ]
encoderParams = [ 'ffmpeg', '-y', '-loglevel', 'quiet' ] encoderParams = [ 'ffmpeg', '-y', '-loglevel', 'quiet' ]
inputParams = [] inputParams = []
codecsParams = [] codecsParams = []
@@ -479,8 +458,6 @@ def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, wid
videoID=0 videoID=0
audioID=0 audioID=0
subTitleID=0 subTitleID=0
audioFiles = {}
imagesPipes = {}
for stream in streams: for stream in streams:
if stream['codec_type'] == 'video': if stream['codec_type'] == 'video':
logger.info("Extracting video stream v:%d" % videoID) logger.info("Extracting video stream v:%d" % videoID)
@@ -506,20 +483,18 @@ def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, wid
# TODO: adjust SAR and DAR # TODO: adjust SAR and DAR
# https://superuser.com/questions/907933/correct-aspect-ratio-without-re-encoding-video-file # https://superuser.com/questions/907933/correct-aspect-ratio-without-re-encoding-video-file
codec = stream['codec_name'] codec = stream['codec_name']
imagesBytes = extractPictures(inputFile=inputFile, begin=begin, nbFrames=nbFrames, width=width, height=height) imagesBytes, memfd = extractPictures(inputFile=inputFile, begin=begin, nbFrames=nbFrames, width=width, height=height)
if dumpPictures: if imagesBytes == None:
sys.exit(-1)
if dumpMemFD:
dumpPPM(imagesBytes, '%s-%d' % (filesPrefix,videoID), temporaries) dumpPPM(imagesBytes, '%s-%d' % (filesPrefix,videoID), temporaries)
# imagesBytes contains now a buffer of bytes that represents the pictures that have been dumped by ffmpeg. # We rewind to zero the memory file descriptor
fdr, fdw = os.pipe() lseek(memfd, 0, SEEK_SET)
os.set_inheritable(fdr, True) set_inheritable(memfd, True)
# The writalbe end of the pipe (fdw) must not be stayed opened in ffmpeg child, otherwise ffmpeg will not be able
# to detect the end of pictures data sent by the other end of the pipe it is reading from (fdr). inputParams.extend(['-framerate', '%f'%frameRate, '-f', 'image2pipe', '-i', '/proc/self/fd/%d' % memfd])
# We manually force non inheritance to be sure (although this should be the case since Python 3.4).
os.set_inheritable(fdw, False)
logger.debug("Creating pipes for images: r:%d w:%d" % (fdr,fdw))
imagesPipes[videoID] = (imagesBytes, fdr, fdw)
inputParams.extend(['-framerate', '%f'%frameRate, '-f', 'image2pipe', '-i', '/proc/self/fd/%d' % fdr])
codecsParams.extend(['-c:v:%d' % videoID, codec, '-pix_fmt', pixelFormat, '-colorspace:v:%d' % videoID, colorSpace, '-color_primaries:v:%d' % videoID, colorPrimaries, codecsParams.extend(['-c:v:%d' % videoID, codec, '-pix_fmt', pixelFormat, '-colorspace:v:%d' % videoID, colorSpace, '-color_primaries:v:%d' % videoID, colorPrimaries,
'-color_trc:v:%d' % videoID, colorTransfer, '-color_range:v:%d' % videoID, colorRange]) '-color_trc:v:%d' % videoID, colorTransfer, '-color_range:v:%d' % videoID, colorRange])
videoID=videoID+1 videoID=videoID+1
@@ -535,14 +510,31 @@ def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, wid
packets = getFramesInStream(inputFile=inputFile, begin=begin, end=end, streamKind='a', subStreamId=audioID) packets = getFramesInStream(inputFile=inputFile, begin=begin, end=end, streamKind='a', subStreamId=audioID)
nbPackets = len(packets) nbPackets = len(packets)
logger.debug("Found %d packets to be extracted from audio track." % nbPackets) logger.debug("Found %d packets to be extracted from audio track." % nbPackets)
try: if(nbPackets > 0):
audioFiles[audioID] = open('%s-%d.pcm' % (filesPrefix,audioID), 'w') packetDuration = packets[0]['duration']
except IOError:
logger.error('Impossible to create file: %s-%d.pcm' % (filesPrefix,audioID)) tmpname = '%s-%d.pcm' % (filesPrefix,audioID)
return None
temporaries.append(audioFiles[audioID]) soundBytes , memfd = extractSound(inputFile=inputFile, begin=begin, nbPackets=nbPackets, packetDuration=packetDuration, outputFileName=tmpname, sampleRate=sampleRate, nbChannels=nbChannels)
extractSound(inputFile=inputFile, begin=begin, nbPackets=nbPackets, outputFile=audioFiles[audioID], sampleRate=sampleRate, nbChannels=nbChannels)
inputParams.extend(['-f', 's32le', '-ar', '%d'%sampleRate, '-ac', '%d'%nbChannels, '-i', '/proc/self/fd/%d' % audioFiles[audioID].fileno()]) if dumpMemFD:
try:
output = open(tmpname,'w')
except IOError:
logger.error('Impossible to create file: %s' % tmpname)
return None
outfd = output.fileno()
pos = 0
while pos < len(soundBytes):
pos+=write(outfd, soundBytes[pos:])
temporaries.append(output)
# We rewind to zero the memory file descriptor
lseek(memfd, 0, SEEK_SET)
set_inheritable(memfd, True)
inputParams.extend(['-f', 's32le', '-ar', '%d'%sampleRate, '-ac', '%d'%nbChannels, '-i', '/proc/self/fd/%d' % memfd])
codecsParams.extend(['-c:a:%d' % audioID, codec, '-b:a:%d' % audioID, '%d' % bitRate]) codecsParams.extend(['-c:a:%d' % audioID, codec, '-b:a:%d' % audioID, '%d' % bitRate])
audioID=audioID+1 audioID=audioID+1
elif stream['codec_type'] == 'subtitle': elif stream['codec_type'] == 'subtitle':
@@ -570,42 +562,18 @@ def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, wid
return None return None
outfd = output.fileno() outfd = output.fileno()
os.set_inheritable(outfd, True) set_inheritable(outfd, True)
# TODO: manage interlaced to previous parameters. # TODO: manage interlaced to previous parameters.
encoderParams.extend(['-top', '1', '-flags:v', '+ilme+ildct', '-bsf:v', 'h264_mp4toannexb,dump_extra=freq=keyframe', '-f', 'matroska', '/proc/self/fd/%d' % outfd]) encoderParams.extend(['-top', '1', '-flags:v', '+ilme+ildct', '-bsf:v', 'h264_mp4toannexb,dump_extra=freq=keyframe', '-f', 'matroska', '/proc/self/fd/%d' % outfd])
logger.info('Encoding video: %s' % fileName) logger.info('Encoding video: %s' % fileName)
with Popen(encoderParams, stdout=PIPE, close_fds=False) as ffmpeg: with Popen(encoderParams, stdout=PIPE, close_fds=False) as ffmpeg:
pos = {} status = ffmpeg.wait()
totalLength = 0 if status != 0:
for vid in range(videoID): logger.error('Encoding failed with status code: %d' % status)
pos[vid]=0 return None
img, fdr, _ = imagesPipes[vid]
# We close the end of the pipe used by ffmepg to read data.
os.close(fdr)
totalLength+=len(img)
length = 0
pg = trange(totalLength)
while length<totalLength:
for vid in range(videoID):
img, _, fdw = imagesPipes[vid]
nbBytes = os.write(fdw, img[pos[vid]:])
pos[vid]=pos[vid]+nbBytes
length+=nbBytes
pg.update(length)
for vid in range(videoID):
_, _, fdw = imagesPipes[vid]
os.close(fdw)
for line in TextIOWrapper(ffmpeg.stdout, encoding="utf-8"):
logger.debug(line)
status = ffmpeg.wait()
if status != 0:
logger.error('Encoding failed with status code: %d' % status)
temporaries.append(output) temporaries.append(output)
return output return output
@@ -626,7 +594,7 @@ def mergeMKVs(inputs, outputName):
outfd = out.fileno() outfd = out.fileno()
fds.append(outfd) fds.append(outfd)
os.set_inheritable(outfd, True) set_inheritable(outfd, True)
mergeParams = ['mkvmerge'] mergeParams = ['mkvmerge']
first = True first = True
@@ -634,7 +602,7 @@ def mergeMKVs(inputs, outputName):
if mkv !=None: if mkv !=None:
fd = mkv.fileno() fd = mkv.fileno()
fds.append(fd) fds.append(fd)
os.set_inheritable(fd, True) set_inheritable(fd, True)
if first: if first:
mergeParams.append('/proc/self/fd/%d' % fd) mergeParams.append('/proc/self/fd/%d' % fd)
first = False first = False
@@ -667,7 +635,7 @@ def mergeMKVs(inputs, outputName):
logger.error('Extraction returns errors') logger.error('Extraction returns errors')
for fd in fds: for fd in fds:
os.set_inheritable(fd, False) set_inheritable(fd, False)
return out return out
@@ -704,7 +672,7 @@ def main():
parser.add_argument("-o", "--output", dest='outputFile', type=str, required=True, help="Output MKV file to produce.") parser.add_argument("-o", "--output", dest='outputFile', type=str, required=True, help="Output MKV file to produce.")
parser.add_argument("-p", "--part", dest='parts', nargs='+', required=False, action='append', metavar="hh:mm:ss[.mmm]-hh:mm:ss[.mmm]", help="Extract this exact part of the original file.") parser.add_argument("-p", "--part", dest='parts', nargs='+', required=False, action='append', metavar="hh:mm:ss[.mmm]-hh:mm:ss[.mmm]", help="Extract this exact part of the original file.")
parser.add_argument("-k", "--keep", action='store_true', help="Do not cleanup temporary files after processing.") parser.add_argument("-k", "--keep", action='store_true', help="Do not cleanup temporary files after processing.")
parser.add_argument("--dump-pictures", action='store_true', dest='dump', help="For debug purpose, dump pictures of headers (and trailers) before (after) each part. They are kept in memory only otherwise.") parser.add_argument("--dump-memory", action='store_true', dest='dump', help="For debug purpose, dump all memory mapping of headers (and trailers) before (after) each part. They are kept in memory only otherwise.")
parser.add_argument("-s","--srt", action='store_true', dest='srt', help="Dump subtitles ") parser.add_argument("-s","--srt", action='store_true', dest='srt', help="Dump subtitles ")
args = parser.parse_args() args = parser.parse_args()
@@ -853,7 +821,7 @@ def main():
if nbHeadFrames > 0: if nbHeadFrames > 0:
# We extract all frames between the beginning upto the frame that immediately preceeds the I-frame. # We extract all frames between the beginning upto the frame that immediately preceeds the I-frame.
head = extractAllStreams(inputFile=mkv, begin=ts1, end=headIFrameTS, nbFrames=nbHeadFrames-1, filesPrefix='part-%d-head' % (partnum), streams=streams, width=width, height=height, temporaries=temporaries, dumpPictures=args.dump) head = extractAllStreams(inputFile=mkv, begin=ts1, end=headIFrameTS, nbFrames=nbHeadFrames-1, filesPrefix='part-%d-head' % (partnum), streams=streams, width=width, height=height, temporaries=temporaries, dumpMemFD=args.dump)
subparts.append(head) subparts.append(head)
# Creating MKV file that corresponds to current part between I-frames # Creating MKV file that corresponds to current part between I-frames
@@ -868,7 +836,7 @@ def main():
if nbTailFrames > 0: if nbTailFrames > 0:
# We extract all frames between the I-frame (including it) upto the end. # We extract all frames between the I-frame (including it) upto the end.
tail = extractAllStreams(inputFile=mkv, begin=tailIFrameTS, end=ts2, nbFrames=nbTailFrames, filesPrefix='part-%d-tail' % (partnum), streams=streams, width=width, height=height, temporaries=temporaries, dumpPictures=args.dump) tail = extractAllStreams(inputFile=mkv, begin=tailIFrameTS, end=ts2, nbFrames=nbTailFrames, filesPrefix='part-%d-tail' % (partnum), streams=streams, width=width, height=height, temporaries=temporaries, dumpMemFD=args.dump)
subparts.append(tail) subparts.append(tail)
logger.info('Merging: %s' % subparts) logger.info('Merging: %s' % subparts)
@@ -908,19 +876,19 @@ def main():
else: else:
logger.error("Dropping subtitle: %s because it is missing language indication") logger.error("Dropping subtitle: %s because it is missing language indication")
for lang in sts: for lang in sts:
indexes = sts[lang] indexes = sts[lang]
if len(indexes) == 0: if len(indexes) == 0:
# Nothing to do. This should not happen. # Nothing to do. This should not happen.
continue continue
if len(indexes) == 1: if len(indexes) == 1:
index = indexes[0] index = indexes[0]
filename = 'essai-%s.srt' % lang filename = 'essai-%s.srt' % lang
elif len(indexes) > 1: elif len(indexes) > 1:
nbsrt = 1 nbsrt = 1
for index in indexes: for index in indexes:
filename = 'essai-%s-%d.srt' % (lang, nbsrt) filename = 'essai-%s-%d.srt' % (lang, nbsrt)
nbsrt+=1 nbsrt+=1
if not args.keep: if not args.keep:
logger.info("Cleaning temporary files") logger.info("Cleaning temporary files")
@@ -928,7 +896,7 @@ def main():
path = os.path.realpath(f.name) path = os.path.realpath(f.name)
logger.info("Removing: %s" % path) logger.info("Removing: %s" % path)
f.close() f.close()
os.unlink(path) unlink(path)
for c in checks: for c in checks:
logger.info("Please check cut smoothness at: %s" % c) logger.info("Please check cut smoothness at: %s" % c)