Open X-SZM opened 1 year ago
It's because of filtergraph escaping. You can find more on the docs. Spent about an hour banging my head against a wall until I finally got it. This code is what got it for me:
path_converter = lambda path: path.replace("\\", "/").replace(":", "\:/").replace(" ", "\\ ").replace("(", "\\(").replace(")", "\\)").replace("[", "\\[").replace("]", "\\]").replace("'", "'\\''")
so instead of subtitles="subtitles.mkv" or whatever do subtitles=path_converter(path_to_subtitles)
@ef1500 I'm curious how you are using this together with the rest of your code. I'm trying to use your solution but not having any luck with mine.
My filename:
[SubsPlease] 16bit Sensation - Another Layer - 01 (720p) [FDA8E9E6].mkv
Using regular ffmpeg CLI, I need to escape only the brackets like so:
-vf subtitles="\[SubsPlease\] 16bit Sensation - Another Layer - 01 (720p) \[FDA8E9E6\].mkv"
However, I still see an error in ffmpeg-python trying your solution.
It seems to be related to loading the file from a different directory than CWD when using the full file path with drive included.
I tested by changing the file to just file.mkv
which caused the same issue in loading the filter.
Test 1 Code:
out_file = 'out.webm'
start_time = '00:01:18.400'
end_time = '00:01:29.750'
in_dir = 'I:/'
in_file = os.path.join(in_dir,'file.mkv')
in_put = ffmpeg.input(in_file,ss=start_time,to=end_time)
audio = in_put.audio
video = in_put.video.filter('subtitles',in_file,si=0)
out = ffmpeg.output(audio,video,filename=out_file,acodec='libopus',vcodec='vp9').overwrite_output()
ffmpeg.run(out)
Test 1 Error:
[Parsed_subtitles_0 @ 00000186a3a856c0] Unable to open I\:/file.mkv
[AVFilterGraph @ 00000186a26a7f80] Error initializing filters
Error initializing complex filters.
Invalid argument
Test 2 Code:
path_converter = lambda path: path.replace("\\", "/").replace(":", "\:/").replace(" ", "\\ ").replace("(", "\\(").replace(")", "\\)").replace("[", "\\[").replace("]", "\\]").replace("'", "'\\''")
out_file = 'out.webm'
start_time = '00:01:18.400'
end_time = '00:01:29.750'
in_dir = 'I:/'
in_file = os.path.join(in_dir,'file.mkv')
in_put = ffmpeg.input(in_file,ss=start_time,to=end_time)
audio = in_put.audio
video = in_put.video.filter('subtitles',path_converter(in_file),si=0)
out = ffmpeg.output(audio,video,filename=out_file,acodec='libopus',vcodec='vp9').overwrite_output()
ffmpeg.run(out)
Test 2 Error:
[Parsed_subtitles_0 @ 0000025b34ed4bc0] Unable to open I\\\://file.mkv
[AVFilterGraph @ 0000025b33af7e00] Error initializing filters
Error initializing complex filters.
Invalid argument
Test 3 Code: (file moved to CWD of python script)
out_file = 'out.webm'
start_time = '00:01:18.400'
end_time = '00:01:29.750'
in_file = 'file.mkv'
in_put = ffmpeg.input(in_file,ss=start_time,to=end_time)
audio = in_put.audio
video = in_put.video.filter('subtitles',in_file,si=0)
out = ffmpeg.output(audio,video,filename=out_file,acodec='libopus',vcodec='vp9').overwrite_output()
ffmpeg.run(out)
Test 3 Successful
For good measure, I also tested with my original filename in CWD. Test 4 Code:
out_file = 'out.webm'
start_time = '00:01:18.400'
end_time = '00:01:29.750'
in_file = '[SubsPlease] 16bit Sensation - Another Layer - 01 (720p) [FDA8E9E6].mkv'
in_put = ffmpeg.input(in_file,ss=start_time,to=end_time)
audio = in_put.audio
video = in_put.video.filter('subtitles',in_file,si=0)
out = ffmpeg.output(audio,video,filename=out_file,acodec='libopus',vcodec='vp9').overwrite_output()
ffmpeg.run(out)
Test 4 Successful
I was using it for a project which would allow me to clip videos for personal use. Sadly, due to my current circumstances, I am not in a position where I am near my project's code and won't be for a few months.
If my memory serves correctly, I think used the path converter to escape a full path from user input via radio webui. I was trying to hardcode the subtitles to my output clip and was unable to do it. I stumbled upon this issue, and after some additional searching, I found some related info on the docs (see here as well)
If you read the docs, it says:
"A first level escaping affects the content of each filter option value, which may contain the special character :
used to separate values, or one of the escaping characters \'
.
A second level escaping affects the whole filter description, which may contain the escaping characters \'
or the special characters [],;
used by the filtergraph description.
Finally, when you specify a filtergraph on a shell commandline, you need to perform a third level escaping for the shell special characters contained within it."
If I had my code, I'd share a snippet, but as I said earlier, I can't access it for the time being. For now, this is really the best I can do to help you.
I appreciate the reply. Unfortunate that I can't see your example, but I'm still trying to get this working.
For now, I think I'm resorting to using a symbolic link created on the fly so I don't need to work with drives in the path.
@X-SZM I am wondering if your issue is also due to the full drive path in your subtitles filter? Have you tested with running the python script from the same folder as your inputs and using relative paths?
EX with removed input directory:
import ffmpeg
video_file = '1_1_1.mp4'
subtl_file = '1_1_1.srt'
(
ffmpeg
.input(video_file)
.filter('subtitles', subtl_file)
.output('output.mp4')
.run()
)
This is what I'm going to be using until I can figure this out: (Or maybe this will just end up being my solution)
import os
import ffmpeg
out_file = 'out.webm'
start_time = '00:01:18.400'
end_time = '00:01:29.750'
in_dir = 'I:/'
in_file = os.path.join(in_dir,'file.mkv')
in_link = 'file.mkv'
if not os.path.isfile(in_link):
os.symlink(in_file, in_link)
input_vid = ffmpeg.input(in_link)
vid = (
input_vid
.filter_('subtitles',in_link)
.trim(start=start_time,end=end_time)
.setpts('PTS-STARTPTS')
)
aud = (
input_vid
.filter_('atrim',start=start_time,end=end_time)
.filter_('asetpts', 'PTS-STARTPTS')
)
joined = ffmpeg.concat(vid, aud, v=1, a=1).node
output = ffmpeg.output(joined['v'], joined['a'], filename=out_file,acodec='libopus',vcodec='vp9').overwrite_output()
output.run()
if os.path.isfile(in_link):
os.remove(in_link)
The escaping for subtitle filters seems overzealous with the drive path. Unsure if it affects other filters, but I had no ill effects from my testing with my other FilterNodes.
The output of the FilterNode
class for .filter('subtitles','I:/file.mkv')
results in subtitles=I\\\\\\:/file.mkv
being passed to ffmpeg.
However, this is causing one extra backslash to be passed to the program as seen in my errors.
[Parsed_subtitles_1 @ 000001f0025056c0] Unable to open I\:/file.mkv
I removed the last escape_chars
call from this class and everything worked as expected.
subtitles=I\\\:/file.mkv
is being passed to ffmpeg with no errors.
No amount of manual pre-escaping was able to get me a successful result with the existing code.
Would be nice to be able to omit escaping like #358 suggests. I think I will stick with my workaround of using symlink creation. Hope this info is helpful to someone that may have this same issues.
I was using it for a project which would allow me to clip videos for personal use. Sadly, due to my current circumstances, I am not in a position where I am near my project's code and won't be for a few months.
@ef1500
hi , I also had similar problem , can you share some full working code , thanks . I tried use the funtion path_converter
you methoned , but still not work.
The escaping for subtitle filters seems overzealous with the drive path. Unsure if it affects other filters, but I had no ill effects from my testing with my other FilterNodes.
The output of the
FilterNode
class for.filter('subtitles','I:/file.mkv')
results insubtitles=I\\\\\\:/file.mkv
being passed to ffmpeg.However, this is causing one extra backslash to be passed to the program as seen in my errors.
[Parsed_subtitles_1 @ 000001f0025056c0] Unable to open I\:/file.mkv
I removed the last
escape_chars
call from this class and everything worked as expected.subtitles=I\\\:/file.mkv
is being passed to ffmpeg with no errors.
For quick reference for any others facing this issue, here's a simple way to implement this solution:
import ffmpeg
### override filter code
def _new_get_filter(self, outgoing_edges):
if self.name == 'subtitles' or self.name == 'ass':
path = ffmpeg._utils.escape_chars(self.args[0], '\\\'=:')
return f"{self.name}='{path}'"
else:
return self._orig_get_filter(outgoing_edges)
setattr(ffmpeg.nodes.FilterNode, '_orig_get_filter', ffmpeg.nodes.FilterNode.__dict__["_get_filter"])
setattr(ffmpeg.nodes.FilterNode, '_get_filter', _new_get_filter)
### now use ffmpeg as normal
input_path = 'C:\\test_video[0].mkv'
output_path = 'C:\\test_video_output.mkv'
ffmpeg.input(input_path).filter('subtitles', input_path).output(
output_path, preset='veryfast', map='0:a').run(overwrite_output=True)
The escaping for subtitle filters seems overzealous with the drive path. Unsure if it affects other filters, but I had no ill effects from my testing with my other FilterNodes. The output of the
FilterNode
class for.filter('subtitles','I:/file.mkv')
results insubtitles=I\\\\\\:/file.mkv
being passed to ffmpeg. However, this is causing one extra backslash to be passed to the program as seen in my errors.[Parsed_subtitles_1 @ 000001f0025056c0] Unable to open I\:/file.mkv
I removed the lastescape_chars
call from this class and everything worked as expected.subtitles=I\\\:/file.mkv
is being passed to ffmpeg with no errors.For quick reference for any others facing this issue, here's a simple way to implement this solution:
import ffmpeg ### override filter code def _new_get_filter(self, outgoing_edges): if self.name == 'subtitles' or self.name == 'ass': path = ffmpeg._utils.escape_chars(self.args[0], '\\\'=:') return f"{self.name}='{path}'" else: return self._orig_get_filter(outgoing_edges) setattr(ffmpeg.nodes.FilterNode, '_orig_get_filter', ffmpeg.nodes.FilterNode.__dict__["_get_filter"]) setattr(ffmpeg.nodes.FilterNode, '_get_filter', _new_get_filter) ### now use ffmpeg as normal input_path = 'C:\\test_video[0].mkv' output_path = 'C:\\test_video_output.mkv' ffmpeg.input(input_path).filter('subtitles', input_path).output( output_path, preset='veryfast', map='0:a').run(overwrite_output=True)
This method worked in the first place solving the path problem. However, adding it somehow won't recognize my force_style settings for the font anymore:
# subtitle
if use_subtitle == True:
combined = combined.filter("subtitles", subtitle_file_processed, force_style="FontName=Microsoft YaHei,FontSize=13")
It will work if I append the escaped "force_style" part from "self._orig_get_filter(outgoing_edges)" to the return value f"{self.name}='{path}'". However, I don't know how to do it in the program
@zh7i
I think the following changes should fix it. Haven't tested it, so perhaps some adjustments may need to be made if any issues occur.
import ffmpeg
### override filter code
def _new_get_filter(self, outgoing_edges):
if self.name in ('subtitles', 'ass'):
s = '\\\'=:'
e = ffmpeg._utils.escape_chars
all_args = [e(a, s) for a in self.args]
all_args.extend(['{}={}'.format(e(k, s), e(v, s)) for k, v in list(self.kwargs.items())])
args_str = ':'.join(all_args)
return f"{self.name}='{args_str}'"
else:
return self._orig_get_filter(outgoing_edges)
setattr(ffmpeg.nodes.FilterNode, '_orig_get_filter', ffmpeg.nodes.FilterNode.__dict__["_get_filter"])
setattr(ffmpeg.nodes.FilterNode, '_get_filter', _new_get_filter)
### now use ffmpeg as normal
input_path = 'C:\\test_video[0].mkv'
output_path = 'C:\\test_video_output.mkv'
ffmpeg.input(input_path).filter('subtitles', input_path).output(
output_path, preset='veryfast', map='0:a').run(overwrite_output=True)
@zh7i
I think the following changes should fix it. Haven't tested it, so perhaps some adjustments may need to be made if any issues occur.
import ffmpeg ### override filter code def _new_get_filter(self, outgoing_edges): if self.name in ('subtitles', 'ass'): s = '\\\'=:' e = ffmpeg._utils.escape_chars all_args = [e(a, s) for a in self.args] all_args.extend(['{}={}'.format(e(k, s), e(v, s)) for k, v in list(self.kwargs.items())]) args_str = ':'.join(all_args) return f"{self.name}='{args_str}'" else: return self._orig_get_filter(outgoing_edges) setattr(ffmpeg.nodes.FilterNode, '_orig_get_filter', ffmpeg.nodes.FilterNode.__dict__["_get_filter"]) setattr(ffmpeg.nodes.FilterNode, '_get_filter', _new_get_filter) ### now use ffmpeg as normal input_path = 'C:\\test_video[0].mkv' output_path = 'C:\\test_video_output.mkv' ffmpeg.input(input_path).filter('subtitles', input_path).output( output_path, preset='veryfast', map='0:a').run(overwrite_output=True)
@voidpenguin-28 I just verified it worked for me. Thanks a lot!
Here's my python code:
I'm sure all the files are in the right place: video in "C:\Users\27433\Desktop\1\1_1_1.mp4", subtitle in "C:\Users\27433\Desktop\1\1_1_1.srt" And the srt file's encoding is UTF-8, But the ffmpeg reported error:
Then I tried to replace the "\"to "\"or"/",but the error are still there, And I also tried to use ``` file_path=r"C:\Users\27433\Desktop\1\1_1_1.mp4" subtitle_path=r"C:\Users\27433\Desktop\1\1_1_1.srt" output_path=r"C:\Users\27433\Desktop\1\output.mp4" os.system("ffmpeg.EXE -i {} -vf subtitles={} {}".format(file_path, subtitle_path, output_path))
[subtitles @ 0000027c04691ac0] Unable to parse option value "Users27433Desktop11_1_1.srt" as image size Last message repeated 1 times [subtitles @ 0000027c04691ac0] Error setting option original_size to value Users27433Desktop11_1_1.srt. [Parsed_subtitles_0 @ 0000027c04600600] Error applying options to the filter. [AVFilterGraph @ 0000027c091ac300] Error initializing filter 'subtitles' with args 'C:Users27433Desktop11_1_1.srt' Error reinitializing filters! Failed to inject frame into filter network: Invalid argument Error while processing the decoded data for stream #0:0 [aac @ 0000027c041d0bc0] Qavg: 11856.707 [aac @ 0000027c041d0bc0] 2 frames left in the queue on closing Conversion failed!
[subtitles @ 0000018074536a80] Unable to parse option value "Users27433Desktop11_1_1.srt" as image size Last message repeated 1 times [subtitles @ 0000018074536a80] Error setting option original_size to value Users27433Desktop11_1_1.srt. [Parsed_subtitles_0 @ 0000018071885ec0] Error applying options to the filter. [AVFilterGraph @ 0000018076c88900] Error initializing filter 'subtitles' with args 'C:\Users\27433\Desktop\1\1_1_1.srt' Error reinitializing filters!
[subtitles @ 000002d7e9858ac0] Unable to parse option value "/Users/27433/Desktop/1/1_1_1.srt" as image size Last message repeated 1 times [subtitles @ 000002d7e9858ac0] Error setting option original_size to value /Users/27433/Desktop/1/1_1_1.srt. [Parsed_subtitles_0 @ 000002d7e9c96bc0] Error applying options to the filter. [AVFilterGraph @ 000002d7ef088a00] Error initializing filter 'subtitles' with args 'C:/Users/27433/Desktop/1/1_1_1.srt'