Improve linting by remove trailing spaces.
This commit is contained in:
161
removeads.py
161
removeads.py
@@ -93,11 +93,11 @@ def getTesseractSupportedLang(tesseract):
|
|||||||
|
|
||||||
def getFrameRate(ffprobe, inputFile):
|
def getFrameRate(ffprobe, inputFile):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
infd = inputFile.fileno()
|
infd = inputFile.fileno()
|
||||||
lseek(infd, 0, SEEK_SET)
|
lseek(infd, 0, SEEK_SET)
|
||||||
set_inheritable(infd, True)
|
set_inheritable(infd, True)
|
||||||
|
|
||||||
meanDuration = 0.
|
meanDuration = 0.
|
||||||
nbFrames1 = 0
|
nbFrames1 = 0
|
||||||
nbFrames2 = 0
|
nbFrames2 = 0
|
||||||
@@ -105,7 +105,7 @@ def getFrameRate(ffprobe, inputFile):
|
|||||||
minTs = None
|
minTs = None
|
||||||
maxTs = None
|
maxTs = None
|
||||||
interlaced = False
|
interlaced = False
|
||||||
|
|
||||||
params = [ffprobe, '-loglevel', 'quiet', '-select_streams', 'v', '-show_frames',
|
params = [ffprobe, '-loglevel', 'quiet', '-select_streams', 'v', '-show_frames',
|
||||||
'-read_intervals', '00%+30', '-of', 'json', '/proc/self/fd/%d' % infd]
|
'-read_intervals', '00%+30', '-of', 'json', '/proc/self/fd/%d' % infd]
|
||||||
env = {**os.environ, 'LANG': 'C'}
|
env = {**os.environ, 'LANG': 'C'}
|
||||||
@@ -133,13 +133,13 @@ def getFrameRate(ffprobe, inputFile):
|
|||||||
nbFrames2+=1
|
nbFrames2+=1
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ffprobe.wait()
|
ffprobe.wait()
|
||||||
|
|
||||||
if ffprobe.returncode != 0:
|
if ffprobe.returncode != 0:
|
||||||
logger.error("ffprobe returns an error code: %d", ffprobe.returncode)
|
logger.error("ffprobe returns an error code: %d", ffprobe.returncode)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
frameRate1 = nbFrames1/(maxTs-minTs)
|
frameRate1 = nbFrames1/(maxTs-minTs)
|
||||||
frameRate2 = nbFrames2 / meanDuration
|
frameRate2 = nbFrames2 / meanDuration
|
||||||
|
|
||||||
@@ -177,34 +177,34 @@ def getSubTitlesTracks(ffprobe, mkvPath):
|
|||||||
else:
|
else:
|
||||||
l = tracks[lang]
|
l = tracks[lang]
|
||||||
l.append(index)
|
l.append(index)
|
||||||
tracks[lang] = l
|
tracks[lang] = l
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ffprobe.wait()
|
ffprobe.wait()
|
||||||
|
|
||||||
if ffprobe.returncode != 0:
|
if ffprobe.returncode != 0:
|
||||||
logger.error("ffprobe returns an error code: %d", ffprobe.returncode)
|
logger.error("ffprobe returns an error code: %d", ffprobe.returncode)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return tracks
|
return tracks
|
||||||
|
|
||||||
def extractSRT(mkvextract, fileName, subtitles, langs):
|
def extractSRT(mkvextract, fileName, subtitles, langs):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
params = [mkvextract, fileName, 'tracks']
|
params = [mkvextract, fileName, 'tracks']
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
|
|
||||||
for lang in subtitles:
|
for lang in subtitles:
|
||||||
iso = Lang(lang)
|
iso = Lang(lang)
|
||||||
|
|
||||||
if iso in langs:
|
if iso in langs:
|
||||||
ocrlang = langs[iso]
|
ocrlang = langs[iso]
|
||||||
else:
|
else:
|
||||||
logger.warning("Language not supported by Tesseract: %s", iso.name)
|
logger.warning("Language not supported by Tesseract: %s", iso.name)
|
||||||
ocrlang ='osd'
|
ocrlang ='osd'
|
||||||
|
|
||||||
if len(subtitles[lang]) == 1:
|
if len(subtitles[lang]) == 1:
|
||||||
params.append('%d:%s' % (subtitles[lang][0], lang))
|
params.append('%d:%s' % (subtitles[lang][0], lang))
|
||||||
res.append(('%s.idx' % lang, '%s.sub' % lang, lang, ocrlang))
|
res.append(('%s.idx' % lang, '%s.sub' % lang, lang, ocrlang))
|
||||||
@@ -230,9 +230,9 @@ def extractSRT(mkvextract, fileName, subtitles, langs):
|
|||||||
pb.update(100-pb.n)
|
pb.update(100-pb.n)
|
||||||
pb.refresh()
|
pb.refresh()
|
||||||
pb.close()
|
pb.close()
|
||||||
|
|
||||||
extract.wait()
|
extract.wait()
|
||||||
|
|
||||||
# mkvextract returns 0, 1 or 2 as error code.
|
# mkvextract returns 0, 1 or 2 as error code.
|
||||||
if extract.returncode == 0:
|
if extract.returncode == 0:
|
||||||
logger.info('Subtitle tracks were succesfully extracted.')
|
logger.info('Subtitle tracks were succesfully extracted.')
|
||||||
@@ -243,11 +243,11 @@ def extractSRT(mkvextract, fileName, subtitles, langs):
|
|||||||
else:
|
else:
|
||||||
logger.error('Mkvextract returns an error code: %d', extract.returncode)
|
logger.error('Mkvextract returns an error code: %d', extract.returncode)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def doOCR(vobsubocr, idxs, duration, temporaries, dumpMemFD=False):
|
def doOCR(vobsubocr, idxs, duration, temporaries, dumpMemFD=False):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
res = []
|
res = []
|
||||||
|
|
||||||
for idxName, subName, lang, iso in idxs:
|
for idxName, subName, lang, iso in idxs:
|
||||||
srtname = '%s.srt' % os.path.splitext(idxName)[0]
|
srtname = '%s.srt' % os.path.splitext(idxName)[0]
|
||||||
# Tesseract seems to recognize the three dots ... as "su"
|
# Tesseract seems to recognize the three dots ... as "su"
|
||||||
@@ -263,7 +263,7 @@ def doOCR(vobsubocr, idxs, duration, temporaries, dumpMemFD=False):
|
|||||||
write(srtfd, '...'.encode(encoding='UTF-8'))
|
write(srtfd, '...'.encode(encoding='UTF-8'))
|
||||||
else:
|
else:
|
||||||
write(srtfd, line.encode(encoding='UTF-8'))
|
write(srtfd, line.encode(encoding='UTF-8'))
|
||||||
|
|
||||||
m = re.match(timestamps, line)
|
m = re.match(timestamps, line)
|
||||||
if m!=None:
|
if m!=None:
|
||||||
hours = int(m.group('hours'))
|
hours = int(m.group('hours'))
|
||||||
@@ -274,7 +274,7 @@ def doOCR(vobsubocr, idxs, duration, temporaries, dumpMemFD=False):
|
|||||||
pb.update()
|
pb.update()
|
||||||
|
|
||||||
status = ocr.wait()
|
status = ocr.wait()
|
||||||
|
|
||||||
if status != 0:
|
if status != 0:
|
||||||
logger.error('OCR failed with status code: %d', status)
|
logger.error('OCR failed with status code: %d', status)
|
||||||
|
|
||||||
@@ -286,7 +286,7 @@ def doOCR(vobsubocr, idxs, duration, temporaries, dumpMemFD=False):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
lseek(srtfd, 0, SEEK_SET)
|
lseek(srtfd, 0, SEEK_SET)
|
||||||
srtLength = fstat(srtfd).st_size
|
srtLength = fstat(srtfd).st_size
|
||||||
buf = read(srtfd, srtLength)
|
buf = read(srtfd, srtLength)
|
||||||
outfd = dumpSrt.fileno()
|
outfd = dumpSrt.fileno()
|
||||||
pos = 0
|
pos = 0
|
||||||
@@ -319,14 +319,14 @@ class SupportedFormat(IntEnum):
|
|||||||
else:
|
else:
|
||||||
return 'Unsupported format'
|
return 'Unsupported format'
|
||||||
|
|
||||||
# Extract SPS/PPS
|
# Extract SPS/PPS
|
||||||
# https://gitlab.com/mbunkus/mkvtoolnix/-/issues/2390
|
# https://gitlab.com/mbunkus/mkvtoolnix/-/issues/2390
|
||||||
# ffmpeg -i <InputFile (before concatenation)> -c:v copy -an -sn -bsf:v trace_headers -t 0.01 -report -loglevel 0 -f null -
|
# ffmpeg -i <InputFile (before concatenation)> -c:v copy -an -sn -bsf:v trace_headers -t 0.01 -report -loglevel 0 -f null -
|
||||||
|
|
||||||
# Found codec private data using mkvinfo
|
# Found codec private data using mkvinfo
|
||||||
def getCodecPrivateDataFromMKV(mkvinfo, inputFile):
|
def getCodecPrivateDataFromMKV(mkvinfo, inputFile):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
infd = inputFile.fileno()
|
infd = inputFile.fileno()
|
||||||
lseek(infd, 0, SEEK_SET)
|
lseek(infd, 0, SEEK_SET)
|
||||||
set_inheritable(infd, True)
|
set_inheritable(infd, True)
|
||||||
@@ -334,7 +334,7 @@ def getCodecPrivateDataFromMKV(mkvinfo, inputFile):
|
|||||||
env = {**os.environ, 'LANG': 'C'}
|
env = {**os.environ, 'LANG': 'C'}
|
||||||
# Output example
|
# Output example
|
||||||
# Codec's private data: size 48 (H.264 profile: High @L4.0) hexdump 01 64 00 28 ff e1 00 1b 67 64 00 28 ac d9 40 78 04 4f dc d4 04 04 05 00 00 92 ef 00 1d ad a6 1f 16 2d 96 01 00 06 68 fb a3 cb 22 c0 fd f8 f8 00 at 406 size 51 data size 48
|
# Codec's private data: size 48 (H.264 profile: High @L4.0) hexdump 01 64 00 28 ff e1 00 1b 67 64 00 28 ac d9 40 78 04 4f dc d4 04 04 05 00 00 92 ef 00 1d ad a6 1f 16 2d 96 01 00 06 68 fb a3 cb 22 c0 fd f8 f8 00 at 406 size 51 data size 48
|
||||||
|
|
||||||
with Popen([mkvinfo, '-z', '-X', '-P', '/proc/self/fd/%d' % infd ], stdout=PIPE, close_fds=False, env=env) as mkvinfo:
|
with Popen([mkvinfo, '-z', '-X', '-P', '/proc/self/fd/%d' % infd ], stdout=PIPE, close_fds=False, env=env) as mkvinfo:
|
||||||
out, _ = mkvinfo.communicate()
|
out, _ = mkvinfo.communicate()
|
||||||
out = out.decode('utf8')
|
out = out.decode('utf8')
|
||||||
@@ -349,7 +349,7 @@ def getCodecPrivateDataFromMKV(mkvinfo, inputFile):
|
|||||||
found = True
|
found = True
|
||||||
mkvinfo.wait()
|
mkvinfo.wait()
|
||||||
break
|
break
|
||||||
|
|
||||||
if found:
|
if found:
|
||||||
lseek(infd, position, SEEK_SET)
|
lseek(infd, position, SEEK_SET)
|
||||||
data = read(infd, size)
|
data = read(infd, size)
|
||||||
@@ -601,7 +601,7 @@ def writeScalingList(buf, bitPosition, size, matrix, optimized=False):
|
|||||||
# deltas.append(0)
|
# deltas.append(0)
|
||||||
# for delta in deltas:
|
# for delta in deltas:
|
||||||
# bitPosition = writeSignedExpGolomb(buf, bitPosition, delta)
|
# bitPosition = writeSignedExpGolomb(buf, bitPosition, delta)
|
||||||
|
|
||||||
return bitPosition
|
return bitPosition
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -1209,7 +1209,7 @@ class PPS:
|
|||||||
bitPosition = writeUnsignedExpGolomb(buf, bitPosition, v)
|
bitPosition = writeUnsignedExpGolomb(buf, bitPosition, v)
|
||||||
elif self.slice_group_map_type == 2:
|
elif self.slice_group_map_type == 2:
|
||||||
for i in range(0, self.num_slice_groups_minus1):
|
for i in range(0, self.num_slice_groups_minus1):
|
||||||
v = self.top_left[i]
|
v = self.top_left[i]
|
||||||
bitPosition = writeUnsignedExpGolomb(buf, bitPosition, v)
|
bitPosition = writeUnsignedExpGolomb(buf, bitPosition, v)
|
||||||
v = self.bottom_right[i]
|
v = self.bottom_right[i]
|
||||||
bitPosition = writeUnsignedExpGolomb(buf, bitPosition, v)
|
bitPosition = writeUnsignedExpGolomb(buf, bitPosition, v)
|
||||||
@@ -1255,7 +1255,7 @@ class PPS:
|
|||||||
else:
|
else:
|
||||||
logger.info("Writing matrix: %s" % matrix)
|
logger.info("Writing matrix: %s" % matrix)
|
||||||
bitPosition = writeScalingList(buf, bitPosition, 64, matrix)
|
bitPosition = writeScalingList(buf, bitPosition, 64, matrix)
|
||||||
bitPosition = writeSignedExpGolomb(buf, bitPosition, self.second_chroma_qp_index_offset)
|
bitPosition = writeSignedExpGolomb(buf, bitPosition, self.second_chroma_qp_index_offset)
|
||||||
|
|
||||||
bitPosition = writeRBSPTrailingBits(buf, bitPosition)
|
bitPosition = writeRBSPTrailingBits(buf, bitPosition)
|
||||||
|
|
||||||
@@ -1267,7 +1267,7 @@ class AVCDecoderConfiguration:
|
|||||||
AVCProfileIndication:int=0 # u(8)
|
AVCProfileIndication:int=0 # u(8)
|
||||||
profile_compatibility:int=0 # u(8)
|
profile_compatibility:int=0 # u(8)
|
||||||
AVCLevelIndication:int=0 # u(8)
|
AVCLevelIndication:int=0 # u(8)
|
||||||
lengthSizeMinusOne:int=0 # u(2) (0,1 or 3)
|
lengthSizeMinusOne:int=0 # u(2) (0,1 or 3)
|
||||||
numOfSequenceParameterSets:int=0 # u(5)
|
numOfSequenceParameterSets:int=0 # u(5)
|
||||||
sps:dict = field(default_factory=dict)
|
sps:dict = field(default_factory=dict)
|
||||||
numOfPictureParameterSets:int=0 #u(8)
|
numOfPictureParameterSets:int=0 #u(8)
|
||||||
@@ -1413,7 +1413,7 @@ class AVCDecoderConfiguration:
|
|||||||
bitPosition = writeBits(buf, bitPosition, self.bit_depth_chroma_minus8, 3)
|
bitPosition = writeBits(buf, bitPosition, self.bit_depth_chroma_minus8, 3)
|
||||||
bitPosition = writeByte(buf, bitPosition, self.numOfSequenceParameterSetExt)
|
bitPosition = writeByte(buf, bitPosition, self.numOfSequenceParameterSetExt)
|
||||||
for i in range(0, self.numOfSequenceParameterSetExt):
|
for i in range(0, self.numOfSequenceParameterSetExt):
|
||||||
# TODO: dump SPSextended
|
# TODO: dump SPSextended
|
||||||
logger.error('Dumping SPS extended not yet implemented')
|
logger.error('Dumping SPS extended not yet implemented')
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -1477,7 +1477,7 @@ def parseCodecPrivate(codecPrivateData):
|
|||||||
for i in range(0, nbZeroes):
|
for i in range(0, nbZeroes):
|
||||||
length*=256
|
length*=256
|
||||||
length+=(codecPrivateData[3+i])
|
length+=(codecPrivateData[3+i])
|
||||||
bytePosition = 3+nbZeroes
|
bytePosition = 3+nbZeroes
|
||||||
avcconfig = AVCDecoderConfiguration()
|
avcconfig = AVCDecoderConfiguration()
|
||||||
avcconfig.fromBytes(codecPrivateData[bytePosition:])
|
avcconfig.fromBytes(codecPrivateData[bytePosition:])
|
||||||
|
|
||||||
@@ -1586,10 +1586,10 @@ def parseMKVTree(mkvinfo, inputFile):
|
|||||||
# cf http://matroska-org.github.io/libebml/specs.html
|
# cf http://matroska-org.github.io/libebml/specs.html
|
||||||
# It is a Type, Length, Value (TLV) kind of binary file.
|
# It is a Type, Length, Value (TLV) kind of binary file.
|
||||||
# Types are encoded as follows:
|
# Types are encoded as follows:
|
||||||
# 1xxx xxxx - Class A IDs (2^7 -1 possible values)
|
# 1xxx xxxx - Class A IDs (2^7 -1 possible values)
|
||||||
# 01xx xxxx xxxx xxxx - Class B IDs (2^14-1 possible values)
|
# 01xx xxxx xxxx xxxx - Class B IDs (2^14-1 possible values)
|
||||||
# 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-1 possible values)
|
# 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-1 possible values)
|
||||||
# 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-1 possible values)
|
# 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-1 possible values)
|
||||||
# Lengths are encoded as follows:
|
# Lengths are encoded as follows:
|
||||||
# 1xxx xxxx - value 0 to 2^7-2
|
# 1xxx xxxx - value 0 to 2^7-2
|
||||||
# 01xx xxxx xxxx xxxx - value 0 to 2^14-2
|
# 01xx xxxx xxxx xxxx - value 0 to 2^14-2
|
||||||
@@ -1609,7 +1609,7 @@ def getEBMLLength(length):
|
|||||||
elif length <= 2**14-2:
|
elif length <= 2**14-2:
|
||||||
size = 2
|
size = 2
|
||||||
elif length <= 2**21-2:
|
elif length <= 2**21-2:
|
||||||
size = 3
|
size = 3
|
||||||
elif length <= 2**28-2:
|
elif length <= 2**28-2:
|
||||||
size = 4
|
size = 4
|
||||||
elif length <= 2**35-2:
|
elif length <= 2**35-2:
|
||||||
@@ -1639,7 +1639,7 @@ def dumpCodecPrivateData(AVCDecoderConfiguration):
|
|||||||
# Code private element
|
# Code private element
|
||||||
res.extend(b'\x63\xA2')
|
res.extend(b'\x63\xA2')
|
||||||
buf = AVCDecoderConfiguration.toBytes()
|
buf = AVCDecoderConfiguration.toBytes()
|
||||||
logger.debug('AVC configuration bitstream: %s (length: %d))' % (hexdump.dump(buf, sep=':'), len(buf)))
|
logger.debug('AVC configuration bitstream: %s (length: %d))' % (hexdump.dump(buf, sep=':'), len(buf)))
|
||||||
|
|
||||||
EMBLlength = getEBMLLength(len(buf))
|
EMBLlength = getEBMLLength(len(buf))
|
||||||
logger.debug('EMBL encoded length: %s' % (hexdump.dump(EMBLlength, sep=':')))
|
logger.debug('EMBL encoded length: %s' % (hexdump.dump(EMBLlength, sep=':')))
|
||||||
@@ -1672,13 +1672,13 @@ def changeEBMLElementSize(inputFile, position, addendum):
|
|||||||
logger.error('Size of element type cannot be determined: %d', elementType)
|
logger.error('Size of element type cannot be determined: %d', elementType)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
# We seek to size
|
# We seek to size
|
||||||
position+=typeSize
|
position+=typeSize
|
||||||
lseek(infd, position, SEEK_SET)
|
lseek(infd, position, SEEK_SET)
|
||||||
|
|
||||||
buf = read(infd, 1)
|
buf = read(infd, 1)
|
||||||
sizeHead = int.from_bytes(buf, byteorder='big')
|
sizeHead = int.from_bytes(buf, byteorder='big')
|
||||||
logger.info('First byte of size: %x' % sizeHead)
|
logger.info('First byte of size: %x' % sizeHead)
|
||||||
mask=128
|
mask=128
|
||||||
found = False
|
found = False
|
||||||
for i in range(1,9):
|
for i in range(1,9):
|
||||||
@@ -1714,7 +1714,7 @@ def changeEBMLElementSize(inputFile, position, addendum):
|
|||||||
exit(-1)
|
exit(-1)
|
||||||
# The difference of length between old size field and new one.
|
# The difference of length between old size field and new one.
|
||||||
delta = sizeOfNewEncodedSize - sizeOfDataSize
|
delta = sizeOfNewEncodedSize - sizeOfDataSize
|
||||||
fileLength = fstat(infd).st_size
|
fileLength = fstat(infd).st_size
|
||||||
# We seek after actual length field
|
# We seek after actual length field
|
||||||
lseek(infd, position+sizeOfDataSize, SEEK_SET)
|
lseek(infd, position+sizeOfDataSize, SEEK_SET)
|
||||||
# We read the rest of file
|
# We read the rest of file
|
||||||
@@ -1796,7 +1796,7 @@ def changeCodecPrivateData(mkvinfo, inputFile, codecData):
|
|||||||
logger.info(keys)
|
logger.info(keys)
|
||||||
|
|
||||||
delta = futureLength-currentLength
|
delta = futureLength-currentLength
|
||||||
# if there is no modification of the private codec data, no need to change anything.
|
# if there is no modification of the private codec data, no need to change anything.
|
||||||
if delta != 0:
|
if delta != 0:
|
||||||
for i in range(0, len(keys)-1):
|
for i in range(0, len(keys)-1):
|
||||||
keys.pop()
|
keys.pop()
|
||||||
@@ -2034,9 +2034,9 @@ def ffmpegConvert(ffmpeg, ffprobe, inputFile, inputFormat, outputFile, outputFor
|
|||||||
else:
|
else:
|
||||||
log = [ '-loglevel', 'quiet' ]
|
log = [ '-loglevel', 'quiet' ]
|
||||||
|
|
||||||
params = [ffmpeg, '-y',]+log+['-progress', '/dev/stdout', '-canvas_size', '%dx%d' % (width, height), '-f', inputFormat, '-i', '/proc/self/fd/%d' % infd,
|
params = [ffmpeg, '-y',]+log+['-progress', '/dev/stdout', '-canvas_size', '%dx%d' % (width, height), '-f', inputFormat, '-i', '/proc/self/fd/%d' % infd,
|
||||||
'-map', '0:v', '-map', '0:a']
|
'-map', '0:v', '-map', '0:a']
|
||||||
if subtitles:
|
if subtitles:
|
||||||
params.extend(['-map', '0:s'])
|
params.extend(['-map', '0:s'])
|
||||||
params.extend(['-bsf:v', 'h264_mp4toannexb,dump_extra=freq=keyframe', '-vcodec', 'copy', '-acodec', 'copy'])
|
params.extend(['-bsf:v', 'h264_mp4toannexb,dump_extra=freq=keyframe', '-vcodec', 'copy', '-acodec', 'copy'])
|
||||||
if subtitles:
|
if subtitles:
|
||||||
@@ -2089,11 +2089,11 @@ def getFramesInStream(ffprobe, inputFile, begin, end, streamKind, subStreamId=0)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
infd = inputFile.fileno()
|
infd = inputFile.fileno()
|
||||||
set_inheritable(infd, True)
|
set_inheritable(infd, True)
|
||||||
|
|
||||||
command = [ffprobe, '-loglevel', 'quiet', '-read_intervals', ('%s%%%s' %(begin, end)), '-show_entries', 'frame',
|
command = [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]
|
'-select_streams', '%s:%d' % (streamKind, subStreamId), '-of', 'json', '/proc/self/fd/%d' % infd]
|
||||||
logger.debug('Executing: %s' % command)
|
logger.debug('Executing: %s', command)
|
||||||
|
|
||||||
with Popen(command, stdout=PIPE, close_fds=False) as ffprobe:
|
with Popen(command, stdout=PIPE, close_fds=False) as ffprobe:
|
||||||
out, _ = ffprobe.communicate()
|
out, _ = ffprobe.communicate()
|
||||||
frames = json.load(BytesIO(out))
|
frames = json.load(BytesIO(out))
|
||||||
@@ -2101,7 +2101,7 @@ def getFramesInStream(ffprobe, inputFile, begin, end, streamKind, subStreamId=0)
|
|||||||
if status != 0:
|
if status != 0:
|
||||||
logger.error('ffprobe failed with status code: %d' % status)
|
logger.error('ffprobe failed with status code: %d' % status)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Sort frames by timestamp
|
# Sort frames by timestamp
|
||||||
tmp = {}
|
tmp = {}
|
||||||
if 'frames' in frames:
|
if 'frames' in frames:
|
||||||
@@ -2112,33 +2112,33 @@ def getFramesInStream(ffprobe, inputFile, begin, end, streamKind, subStreamId=0)
|
|||||||
return None
|
return None
|
||||||
if begin <= ts and ts <= end:
|
if begin <= ts and ts <= end:
|
||||||
tmp[ts]=frame
|
tmp[ts]=frame
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
for ts in sorted(tmp):
|
for ts in sorted(tmp):
|
||||||
res.append(tmp[ts])
|
res.append(tmp[ts])
|
||||||
|
|
||||||
return res
|
return res
|
||||||
else:
|
else:
|
||||||
logger.error('Impossible to retrieve frames inside file around [%s,%s]' % (begin, end))
|
logger.error('Impossible to retrieve frames inside file around [%s,%s]' % (begin, end))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
def getNearestIDRFrame(ffprobe, inputFile, timestamp, before=True, delta=timedelta(seconds=2)):
|
def getNearestIDRFrame(ffprobe, inputFile, timestamp, before=True, delta=timedelta(seconds=2)):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
zero = timedelta()
|
zero = timedelta()
|
||||||
tbegin = timestamp-delta
|
tbegin = timestamp-delta
|
||||||
tend = timestamp+delta
|
tend = timestamp+delta
|
||||||
if tbegin < zero:
|
if tbegin < zero:
|
||||||
tbegin = zero
|
tbegin = zero
|
||||||
|
|
||||||
infd = inputFile.fileno()
|
infd = inputFile.fileno()
|
||||||
set_inheritable(infd, True)
|
set_inheritable(infd, True)
|
||||||
|
|
||||||
logger.debug('Looking for IDR frame in [%s, %s]', tbegin, tend)
|
logger.debug('Looking for IDR frame in [%s, %s]', tbegin, tend)
|
||||||
|
|
||||||
idrs = []
|
idrs = []
|
||||||
|
|
||||||
# Retains only IDR frame
|
# Retains only IDR frame
|
||||||
with Popen([ffprobe, '-loglevel', 'quiet', '-read_intervals', ('%s%%%s' %(tbegin, tend)), '-skip_frame', 'nokey', '-show_entries', 'frame', '-select_streams', 'v:0', '-of', 'json', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe:
|
with Popen([ffprobe, '-loglevel', 'quiet', '-read_intervals', ('%s%%%s' %(tbegin, tend)), '-skip_frame', 'nokey', '-show_entries', 'frame', '-select_streams', 'v:0', '-of', 'json', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe:
|
||||||
out, _ = ffprobe.communicate()
|
out, _ = ffprobe.communicate()
|
||||||
@@ -2159,16 +2159,13 @@ def getNearestIDRFrame(ffprobe, inputFile, timestamp, before=True, delta=timedel
|
|||||||
else:
|
else:
|
||||||
logger.error('Impossible to retrieve IDR frames inside file around [%s,%s]', tbegin, tend)
|
logger.error('Impossible to retrieve IDR frames inside file around [%s,%s]', tbegin, tend)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getNearestIFrame(ffprobe, inputFile, timestamp, before=True, deltaMax=timedelta(seconds=15)):
|
def getNearestIFrame(ffprobe, inputFile, timestamp, before=True, deltaMax=timedelta(seconds=15)):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
infd = inputFile.fileno()
|
infd = inputFile.fileno()
|
||||||
set_inheritable(infd, True)
|
set_inheritable(infd, True)
|
||||||
|
|
||||||
delta = timedelta(seconds=1)
|
delta = timedelta(seconds=1)
|
||||||
|
|
||||||
iframe = None
|
iframe = None
|
||||||
@@ -2184,26 +2181,26 @@ def getNearestIFrame(ffprobe, inputFile, timestamp, before=True, deltaMax=timede
|
|||||||
tend = timestamp
|
tend = timestamp
|
||||||
if tbegin < zero:
|
if tbegin < zero:
|
||||||
tbegin = zero
|
tbegin = zero
|
||||||
logger.debug('Looking for an iframe in [%s, %s]' % (tbegin, tend))
|
logger.debug('Looking for an iframe in [%s, %s]', tbegin, tend)
|
||||||
|
|
||||||
frames = getFramesInStream(ffprobe, inputFile=inputFile, begin=tbegin, end=tend, streamKind='v')
|
frames = getFramesInStream(ffprobe, inputFile=inputFile, begin=tbegin, end=tend, streamKind='v')
|
||||||
if frames is None:
|
if frames is None:
|
||||||
logger.debug('Found no frame in [%s, %s]' % (tbegin, tend))
|
logger.debug('Found no frame in [%s, %s]', tbegin, tend)
|
||||||
delta+=timedelta(seconds=1)
|
delta+=timedelta(seconds=1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
iframes = []
|
iframes = []
|
||||||
for frame in frames:
|
for frame in frames:
|
||||||
if frame['pict_type'] == 'I':
|
if frame['pict_type'] == 'I':
|
||||||
iframes.append(frame)
|
iframes.append(frame)
|
||||||
|
|
||||||
found = False
|
found = False
|
||||||
for frame in iframes:
|
for frame in iframes:
|
||||||
ts = getTSFrame(frame)
|
ts = getTSFrame(frame)
|
||||||
if ts is None:
|
if ts is None:
|
||||||
logger.warning('I-frame with no timestamp: %s' % frame)
|
logger.warning('I-frame with no timestamp: %s' % frame)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if before and ts <= timestamp:
|
if before and ts <= timestamp:
|
||||||
found = True
|
found = True
|
||||||
iframe = frame
|
iframe = frame
|
||||||
@@ -2218,7 +2215,7 @@ def getNearestIFrame(ffprobe, inputFile, timestamp, before=True, deltaMax=timede
|
|||||||
else:
|
else:
|
||||||
delta+=timedelta(seconds=1)
|
delta+=timedelta(seconds=1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if iframe is not None:
|
if iframe is not None:
|
||||||
its = getTSFrame(iframe)
|
its = getTSFrame(iframe)
|
||||||
nbFrames = 0
|
nbFrames = 0
|
||||||
@@ -2296,7 +2293,7 @@ def extractPictures(ffmpeg, 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)
|
||||||
|
|
||||||
command = [ffmpeg, '-loglevel', 'quiet' ,'-y', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % infd, '-s', '%dx%d'%(width, height),
|
command = [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 ]
|
'-vframes', '%d'%nbFrames, '-c:v', 'ppm', '-f', 'image2pipe', '/proc/self/fd/%d' % outfd ]
|
||||||
logger.debug('Executing: %s', command)
|
logger.debug('Executing: %s', command)
|
||||||
|
|
||||||
@@ -2310,7 +2307,7 @@ def extractPictures(ffmpeg, inputFile, begin, nbFrames, width=640, height=480):
|
|||||||
lseek(outfd, 0, SEEK_SET)
|
lseek(outfd, 0, SEEK_SET)
|
||||||
images = read(outfd,length)
|
images = read(outfd,length)
|
||||||
if len(images) != length:
|
if len(images) != length:
|
||||||
logger.error("Received %d bytes but %d were expected." % (len(images), length))
|
logger.error("Received %d bytes but %d were expected." % (len(images), length))
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
lseek(outfd, 0, SEEK_SET)
|
lseek(outfd, 0, SEEK_SET)
|
||||||
@@ -2327,7 +2324,7 @@ def extractSound(ffmpeg, inputFile, begin, outputFileName, packetDuration, subCh
|
|||||||
sound = bytes()
|
sound = bytes()
|
||||||
length = int(nbChannels*sampleRate*4*nbPackets*packetDuration/1000)
|
length = int(nbChannels*sampleRate*4*nbPackets*packetDuration/1000)
|
||||||
|
|
||||||
command = [ffmpeg, '-y', '-loglevel', 'quiet', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % infd, '-frames:a:%d' % subChannel, '%d' % (nbPackets+1),
|
command = [ffmpeg, '-y', '-loglevel', 'quiet', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % infd, '-frames:a:%d' % subChannel, '%d' % (nbPackets+1),
|
||||||
'-c:a', 'pcm_s32le', '-sample_rate', '%d' % sampleRate, '-channels', '%d' % nbChannels, '-f', 's32le', '/proc/self/fd/%d' % outfd]
|
'-c:a', 'pcm_s32le', '-sample_rate', '%d' % sampleRate, '-channels', '%d' % nbChannels, '-f', 's32le', '/proc/self/fd/%d' % outfd]
|
||||||
logger.debug('Executing: %s', command)
|
logger.debug('Executing: %s', command)
|
||||||
|
|
||||||
@@ -2349,7 +2346,7 @@ def extractSound(ffmpeg, inputFile, begin, outputFileName, packetDuration, subCh
|
|||||||
def dumpPPM(pictures, prefix, temporaries):
|
def dumpPPM(pictures, prefix, temporaries):
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# "P6\nWIDTH HEIGHT\n255\n"
|
# "P6\nWIDTH HEIGHT\n255\n"
|
||||||
pos = 0
|
pos = 0
|
||||||
picture = 0
|
picture = 0
|
||||||
|
|
||||||
@@ -2362,7 +2359,7 @@ def dumpPPM(pictures, prefix, temporaries):
|
|||||||
dimensions = header.readline().decode('utf8')
|
dimensions = header.readline().decode('utf8')
|
||||||
maxvalue = header.readline().decode('utf8')
|
maxvalue = header.readline().decode('utf8')
|
||||||
if magic == 'P6\n':
|
if magic == 'P6\n':
|
||||||
pattern = re.compile('^(?P<width>[0-9]+) (?P<height>[0-9]+)\n$')
|
pattern = re.compile('^(?P<width>[0-9]+) (?P<height>[0-9]+)\n$')
|
||||||
m = pattern.match(dimensions)
|
m = pattern.match(dimensions)
|
||||||
if m is not None:
|
if m is not None:
|
||||||
width = int(m['width'])
|
width = int(m['width'])
|
||||||
@@ -2372,7 +2369,7 @@ def dumpPPM(pictures, prefix, temporaries):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
logger.error('Not a PPM picture')
|
logger.error('Not a PPM picture')
|
||||||
return
|
return
|
||||||
|
|
||||||
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
|
||||||
try:
|
try:
|
||||||
@@ -2394,7 +2391,7 @@ def extractAllStreams(ffmpeg, ffprobe, inputFile, begin, end, streams, filesPref
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# The command line for encoding only video track
|
# The command line for encoding only video track
|
||||||
videoEncoderParams = [ ffmpeg, '-y', '-loglevel', 'quiet']
|
videoEncoderParams = [ ffmpeg, '-y', '-loglevel', 'quiet']
|
||||||
videoInputParams = []
|
videoInputParams = []
|
||||||
videoCodecParams = []
|
videoCodecParams = []
|
||||||
|
|
||||||
@@ -2419,7 +2416,7 @@ def extractAllStreams(ffmpeg, ffprobe, inputFile, begin, end, streams, filesPref
|
|||||||
colorTransfer = stream['color_transfer']
|
colorTransfer = stream['color_transfer']
|
||||||
colorPrimaries = stream['color_primaries']
|
colorPrimaries = stream['color_primaries']
|
||||||
level = int(stream['level'])
|
level = int(stream['level'])
|
||||||
level = '%d.%d' % (floor(level/10), level%10)
|
level = '%d.%d' % (floor(level/10), level%10)
|
||||||
chromaLocation = stream['chroma_location']
|
chromaLocation = stream['chroma_location']
|
||||||
fieldOrder = stream
|
fieldOrder = stream
|
||||||
interlacedOptions = []
|
interlacedOptions = []
|
||||||
@@ -2627,7 +2624,7 @@ def mergeMKVs(mkvmerge, inputs, outputName, concatenate=True, timestamps=None):
|
|||||||
fd = mkv.fileno()
|
fd = mkv.fileno()
|
||||||
fds.append(fd)
|
fds.append(fd)
|
||||||
set_inheritable(fd, True)
|
set_inheritable(fd, True)
|
||||||
# If we pass a timestamps file associated with the considered track, use it.
|
# If we pass a timestamps file associated with the considered track, use it.
|
||||||
if timestamps is not None and partNum in timestamps:
|
if timestamps is not None and partNum in timestamps:
|
||||||
tsfd = timestamps[partNum].fileno()
|
tsfd = timestamps[partNum].fileno()
|
||||||
lseek(tsfd, 0, SEEK_SET)
|
lseek(tsfd, 0, SEEK_SET)
|
||||||
@@ -2825,7 +2822,7 @@ def concatenateH264Parts(h264parts, output):
|
|||||||
totalLength = 0
|
totalLength = 0
|
||||||
for h264 in h264parts:
|
for h264 in h264parts:
|
||||||
fd = h264.fileno()
|
fd = h264.fileno()
|
||||||
totalLength += fstat(fd).st_size
|
totalLength += fstat(fd).st_size
|
||||||
|
|
||||||
logger.info('Total length: %d', totalLength)
|
logger.info('Total length: %d', totalLength)
|
||||||
|
|
||||||
@@ -2843,7 +2840,7 @@ def concatenateH264Parts(h264parts, output):
|
|||||||
pos = 0
|
pos = 0
|
||||||
while pos < len(buf):
|
while pos < len(buf):
|
||||||
nbBytes = write(outfd, buf[pos:])
|
nbBytes = write(outfd, buf[pos:])
|
||||||
pb.update(nbBytes)
|
pb.update(nbBytes)
|
||||||
pos += nbBytes
|
pos += nbBytes
|
||||||
|
|
||||||
def concatenateH264TSParts(h264TSParts, output):
|
def concatenateH264TSParts(h264TSParts, output):
|
||||||
@@ -3001,7 +2998,7 @@ def main():
|
|||||||
if formatOfFile == SupportedFormat.TS:
|
if formatOfFile == SupportedFormat.TS:
|
||||||
logger.info("Converting TS to MP4 (to fix timestamps).")
|
logger.info("Converting TS to MP4 (to fix timestamps).")
|
||||||
try:
|
try:
|
||||||
with open(mp4filename, 'w+') as mp4:
|
with open(mp4filename, 'w+') as mp4:
|
||||||
ffmpegConvert(paths['ffmpeg'], paths['ffprobe'], inputFile, 'mpegts', mp4, 'mp4', duration)
|
ffmpegConvert(paths['ffmpeg'], paths['ffprobe'], inputFile, 'mpegts', mp4, 'mp4', duration)
|
||||||
temporaries.append(mp4)
|
temporaries.append(mp4)
|
||||||
logger.info("Converting MP4 to MKV.")
|
logger.info("Converting MP4 to MKV.")
|
||||||
@@ -3094,7 +3091,7 @@ def main():
|
|||||||
# Si la trame de début est déjà 'I', il n'y a rien à faire.
|
# Si la trame de début est déjà 'I', il n'y a rien à faire.
|
||||||
# Sinon on extrait les trames 'B' ou 'P' depuis le début jusqu'à la trame 'I' non incluse.
|
# Sinon on extrait les trames 'B' ou 'P' depuis le début jusqu'à la trame 'I' non incluse.
|
||||||
# Si la trame de fin précède une trame I, on n'a rien à faire.
|
# Si la trame de fin précède une trame I, on n'a rien à faire.
|
||||||
# Sinon on extrait toutes les trames depuis la dernière trame I jusqu'à la trame de fin.
|
# Sinon on extrait toutes les trames depuis la dernière trame I jusqu'à la trame de fin.
|
||||||
|
|
||||||
partnum = partnum + 1
|
partnum = partnum + 1
|
||||||
|
|
||||||
@@ -3226,7 +3223,7 @@ def main():
|
|||||||
checks.append(pos)
|
checks.append(pos)
|
||||||
|
|
||||||
# When using coarse option there is a single AVC configuration.
|
# When using coarse option there is a single AVC configuration.
|
||||||
for avcConfig in otherAvcConfigs:
|
for avcConfig in otherAvcConfigs:
|
||||||
mainAvcConfig.merge(avcConfig)
|
mainAvcConfig.merge(avcConfig)
|
||||||
logger.debug('Merged AVC configuration: %s', mainAvcConfig)
|
logger.debug('Merged AVC configuration: %s', mainAvcConfig)
|
||||||
|
|
||||||
@@ -3279,7 +3276,7 @@ def main():
|
|||||||
finalWithVideo = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=[fullH264, finalNoVideo], outputName=finalWithVideoName, concatenate=False, timestamps={0: fullH264TS})
|
finalWithVideo = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=[fullH264, finalNoVideo], outputName=finalWithVideoName, concatenate=False, timestamps={0: fullH264TS})
|
||||||
finalCodecPrivateData = dumpCodecPrivateData(mainAvcConfig)
|
finalCodecPrivateData = dumpCodecPrivateData(mainAvcConfig)
|
||||||
logger.debug('Final codec private data: %s' % hexdump.dump(finalCodecPrivateData, sep=':'))
|
logger.debug('Final codec private data: %s' % hexdump.dump(finalCodecPrivateData, sep=':'))
|
||||||
logger.info('Changing codec private data with the new one.')
|
logger.info('Changing codec private data with the new one.')
|
||||||
changeCodecPrivateData(paths['mkvinfo'], finalWithVideo, finalCodecPrivateData)
|
changeCodecPrivateData(paths['mkvinfo'], finalWithVideo, finalCodecPrivateData)
|
||||||
|
|
||||||
if args.srt:
|
if args.srt:
|
||||||
|
|||||||
Reference in New Issue
Block a user