Adding an option to dump images of trailers and headers of each part for debugging purpose.

This commit is contained in:
Frédéric Tronel
2023-11-30 21:06:50 +01:00
parent 4bd294e26b
commit 685365388e

View File

@@ -359,7 +359,9 @@ def extractPictures(inputFile, begin, nbFrames, width=640, height=480):
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:
while ffmpeg.poll() == None:
fds, _, _ = select([fdr, ffmpeg.stdout], [], [], .1)
# TODO: understand why this line ends up in reading on an already closed file descriptor
# fds, _, _ = select([fdr, ffmpeg.stdout], [], [], .1)
fds, _, _ = select([fdr], [], [], .1)
if fdr in fds:
buf = os.read(fdr, 1000000)
# print("Read %d bytes of image. ffmpeg finished: %s" % (len(buf), ffmpeg.poll()))
@@ -415,11 +417,47 @@ def extractSound(inputFile, begin, outputFile, subChannel=0, nbPackets=0, sample
if status != 0:
logger.error('Sound extraction returns error code: %d' % status)
def dumpPPM(pictures, prefix):
# "P6\nWIDTH HEIGHT\n255\n"
# headerLen=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1
pass
def dumpPPM(pictures, prefix, temporaries):
logger = logging.getLogger(__name__)
# "P6\nWIDTH HEIGHT\n255\n"
pos = 0
picture = 0
while pos<len(pictures):
filename = '%s-%03d.ppm' % (prefix, picture)
header = BytesIO(pictures[pos:])
magic = header.readline().decode('utf8')
dimensions = header.readline().decode('utf8')
maxvalue = header.readline().decode('utf8')
if magic == 'P6\n':
pattern = re.compile('^(?P<width>[0-9]+) (?P<height>[0-9]+)\n$')
m = pattern.match(dimensions)
if m != None:
width = int(m['width'])
height = int(m['height'])
else:
logger.error('Impossible to parse dimensions of picture')
return
else:
logger.error('Not a PPM picture')
return
headerLen=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1
try:
out = open(filename, 'w')
outfd = out.fileno()
except IOError:
logger.error('Impossible to create file: %s' % filename)
temporaries.append(out)
length=headerLen+3*width*height
nbBytes = 0
while nbBytes < length:
nbBytes+=os.write(outfd, pictures[pos+nbBytes:pos+length])
pos+=length
picture+=1
def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, width, height, temporaries, dumpPictures=False):
logger = logging.getLogger(__name__)
# encoderParams = [ 'ffmpeg', '-y', '-loglevel', 'quiet' ]
@@ -460,7 +498,7 @@ def extractAllStreams(inputFile, begin, end, streams, filesPrefix, nbFrames, wid
codec = stream['codec_name']
imagesBytes = extractPictures(inputFile=inputFile, begin=begin, nbFrames=nbFrames, width=width, height=height)
if dumpPictures:
dumpPPM(imagesBytes, '%s-%d' % (filesPrefix,videoID))
dumpPPM(imagesBytes, '%s-%d' % (filesPrefix,videoID), temporaries)
# imagesBytes contains now a buffer of bytes that represents the pictures that have been dumped by ffmpeg.
fdr, fdw = os.pipe()
@@ -634,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("-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("--dump-pictures", action='store_true', 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-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.")
args = parser.parse_args()
logger.debug("Arguments: %s" % args)
@@ -782,7 +820,7 @@ def main():
if nbHeadFrames > 0:
# 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)
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)
subparts.append(head)
# Creating MKV file that corresponds to current part between I-frames
@@ -797,7 +835,7 @@ def main():
if nbTailFrames > 0:
# 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)
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)
subparts.append(tail)
logger.info('Merging: %s' % subparts)