Improve error handling.
This commit is contained in:
+31
-38
@@ -8,12 +8,12 @@ from io import TextIOWrapper
|
||||
from math import ceil, floor, log
|
||||
from os import SEEK_SET, close, lseek, memfd_create, read, set_inheritable, write
|
||||
from subprocess import PIPE, Popen
|
||||
from typing import IO
|
||||
from typing import IO, BinaryIO
|
||||
|
||||
from tqdm import tqdm
|
||||
from typeguard import typechecked
|
||||
|
||||
from tscut.exceptions import ExternalToolError, InvalidMediaError
|
||||
from tscut.exceptions import ExternalToolError, InvalidMediaError, TemporaryFileError
|
||||
from tscut.tools.ffprobe import (
|
||||
get_frames_in_stream,
|
||||
get_video_dimensions,
|
||||
@@ -21,13 +21,14 @@ from tscut.tools.ffprobe import (
|
||||
)
|
||||
from tscut.tools.ppm import dump_ppm
|
||||
from tscut.tools.timeframe import get_packet_duration, parse_timestamp
|
||||
from tscut.temporaries import TemporaryFiles
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@typechecked
|
||||
def ffmpeg_convert(ffmpeg_path:str, ffprobe_path:str, input_file: IO[bytes], input_format:str,
|
||||
output_file: IO[bytes], output_format:str, duration: timedelta):
|
||||
output_file: IO[bytes], output_format:str, duration: timedelta) -> None:
|
||||
width, height = get_video_dimensions(ffprobe_path, input_file)
|
||||
if width is None or height is None:
|
||||
return
|
||||
@@ -68,7 +69,7 @@ def ffmpeg_convert(ffmpeg_path:str, ffprobe_path:str, input_file: IO[bytes], inp
|
||||
pb.update()
|
||||
status = ffmpeg.wait()
|
||||
if status != 0:
|
||||
logger.error('Conversion failed with status code: %d', status)
|
||||
raise ExternalToolError(f"Conversion failed with status code: {status:d}")
|
||||
|
||||
|
||||
@typechecked
|
||||
@@ -116,14 +117,12 @@ def extract_pictures(ffmpeg_path:str, input_file:IO[bytes], begin:timedelta, nb_
|
||||
with Popen(command, stdout=PIPE, close_fds=False) as ffmpeg:
|
||||
status = ffmpeg.wait()
|
||||
if status != 0:
|
||||
logger.error('Conversion failed with status code: %d', status)
|
||||
raise ExternalToolError("")
|
||||
raise ExternalToolError(f"Conversion failed with status code: {status:d}")
|
||||
|
||||
lseek(outfd, 0, SEEK_SET)
|
||||
images = read(outfd,length)
|
||||
if len(images) != length:
|
||||
logger.error("Received %d bytes but %d were expected.", len(images), length)
|
||||
raise InvalidMediaError("")
|
||||
raise InvalidMediaError(f"Received {len(images)} bytes but {length} were expected.")
|
||||
|
||||
lseek(outfd, 0, SEEK_SET)
|
||||
return images, outfd
|
||||
@@ -150,24 +149,25 @@ def extract_sound(ffmpeg_path:str, input_file: IO[bytes], begin:timedelta, outpu
|
||||
with Popen(command, stdout=PIPE, close_fds=False) as ffmpeg:
|
||||
status = ffmpeg.wait()
|
||||
if status != 0:
|
||||
logger.error('Sound extraction returns error code: %d', status)
|
||||
raise ExternalToolError("")
|
||||
raise ExternalToolError(f"Sound extraction returns error code: {status}")
|
||||
|
||||
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, nb_channels, sample_rate, nb_packets,
|
||||
packet_duration)
|
||||
raise InvalidMediaError("")
|
||||
raise InvalidMediaError(f"Received {len(sound)} bytes but {length} were expected (\
|
||||
channels={nb_channels}, freq={sample_rate} packets={nb_packets},\
|
||||
duration={packet_duration} ms).")
|
||||
|
||||
return sound, outfd
|
||||
|
||||
@typechecked
|
||||
def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes], begin:timedelta,
|
||||
end:timedelta, streams, files_prefix, nb_frames:int, framerate:float,
|
||||
width:int, height:int, temporaries, dump_mem_fd:bool=False):
|
||||
width:int, height:int, temporaries:TemporaryFiles,
|
||||
dump_mem_fd:bool=False) -> tuple[BinaryIO|None,
|
||||
TextIOWrapper|None,
|
||||
BinaryIO|None]:
|
||||
# The command line for encoding only video track
|
||||
video_encoder_params = [ ffmpeg_path, '-y', '-loglevel', 'quiet']
|
||||
video_input_params = []
|
||||
@@ -258,8 +258,7 @@ def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes],
|
||||
packets = get_frames_in_stream(ffprobe_path, input_file=input_file, begin=begin,
|
||||
end=end, stream_kind='a', sub_stream_id=audio_id)
|
||||
if packets is None:
|
||||
logger.error("Impossible to retrieve audio packets")
|
||||
return None
|
||||
raise InvalidMediaError("Impossible to retrieve audio packets")
|
||||
nb_packets = len(packets)
|
||||
logger.debug("Found %d packets to be extracted from audio track.", nb_packets)
|
||||
if nb_packets > 0:
|
||||
@@ -283,14 +282,13 @@ def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes],
|
||||
if dump_mem_fd:
|
||||
try:
|
||||
with open(tmpname,'wb') as output:
|
||||
temporaries.append(output)
|
||||
temporaries.add(output)
|
||||
outfd = output.fileno()
|
||||
pos = 0
|
||||
while pos < len(sound_bytes):
|
||||
pos+=write(outfd, sound_bytes[pos:])
|
||||
except OSError:
|
||||
logger.error('Impossible to create file: %s', tmpname)
|
||||
return None
|
||||
except OSError as e:
|
||||
raise TemporaryFileError(f"Impossible to create file: {tmpname}") from e
|
||||
|
||||
# We rewind to zero the memory file descriptor
|
||||
lseek(memfd, 0, SEEK_SET)
|
||||
@@ -323,9 +321,8 @@ def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes],
|
||||
mkv_filename = f'{files_prefix}.mkv'
|
||||
try:
|
||||
mkv_output = open(mkv_filename,'wb+')
|
||||
except OSError:
|
||||
logger.error('Impossible to create file: %s', mkv_filename)
|
||||
return None
|
||||
except OSError as e:
|
||||
raise TemporaryFileError(f"Impossible to create file: {mkv_filename}") from e
|
||||
|
||||
mkvoutfd = mkv_output.fileno()
|
||||
set_inheritable(mkvoutfd, True)
|
||||
@@ -336,17 +333,15 @@ def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes],
|
||||
with Popen(generic_encoder_params, stdout=PIPE, close_fds=False) as ffmpeg:
|
||||
status = ffmpeg.wait()
|
||||
if status != 0:
|
||||
logger.error('Encoding failed with status code: %d', status)
|
||||
return None
|
||||
raise ExternalToolError(f"Encoding failed with status code: {status}")
|
||||
|
||||
temporaries.append(mkv_output)
|
||||
temporaries.add(mkv_output)
|
||||
|
||||
h264_filename = f'{files_prefix}.h264'
|
||||
try:
|
||||
h264_output = open(h264_filename,'wb+')
|
||||
except OSError:
|
||||
logger.error('Impossible to create file: %s', h264_filename)
|
||||
return None
|
||||
except OSError as e:
|
||||
raise TemporaryFileError(f"Impossible to create file {h264_filename}") from e
|
||||
|
||||
h264outfd = h264_output.fileno()
|
||||
set_inheritable(h264outfd, True)
|
||||
@@ -365,17 +360,15 @@ def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes],
|
||||
with Popen(video_encoder_params, stdout=PIPE, close_fds=False) as ffmpeg:
|
||||
status = ffmpeg.wait()
|
||||
if status != 0:
|
||||
logger.error('Encoding failed with status code: %d', status)
|
||||
return None
|
||||
raise ExternalToolError(f"Encoding failed with status code: {status:d}")
|
||||
|
||||
temporaries.append(h264_output)
|
||||
temporaries.add(h264_output)
|
||||
|
||||
h264_ts_filename = f'{files_prefix}-ts.txt'
|
||||
try:
|
||||
h264_ts_output = open(h264_ts_filename,'w+', encoding='utf8')
|
||||
except OSError:
|
||||
logger.error('Impossible to create file: %s', h264_ts_filename)
|
||||
return None
|
||||
except OSError as e:
|
||||
raise TemporaryFileError(f"Impossible to create file: {h264_ts_filename}") from e
|
||||
|
||||
h264_ts_output.write('# timestamp format v2\n')
|
||||
ts = 0
|
||||
@@ -385,7 +378,7 @@ def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes],
|
||||
h264_ts_output.flush()
|
||||
h264_ts_output.seek(0)
|
||||
|
||||
temporaries.append(h264_ts_output)
|
||||
temporaries.add(h264_ts_output)
|
||||
|
||||
for memfd in memfds:
|
||||
close(memfd)
|
||||
@@ -393,5 +386,5 @@ def extract_all_streams(ffmpeg_path:str, ffprobe_path:str, input_file:IO[bytes],
|
||||
return h264_output, h264_ts_output, mkv_output
|
||||
|
||||
# Nothing to be done. We are already at a i-frame boundary.
|
||||
return None, None
|
||||
return None, None, None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user