bash脚本多次调用ffmpeg仅最后一次成功,输出格式报错求解决
看起来你遇到的问题是连续调用ffmpeg时,前几次的命令参数解析出了异常——错误信息里混进了lic_html这种无关内容,明显是参数被意外干扰了。结合你的脚本情况,给你几个具体的解决思路:
问题重现与脚本代码
我写了一个简单的bash脚本用来从M3U8流截取单帧并添加水印生成图片,单次调用ffmpeg的时候一切正常,但连续调用2次及以上时,只有最后一次能成功,前面的调用都会报错:
unable to find suitable output format for /var/.../.../.jpg : Invalid argument
我的脚本代码如下:
#!/bin/bash ffmpeg -y -i "http://xx/22.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/22.jpg ffmpeg -y -i "http://xx/23.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/23.jpg
解决思路
强制关闭ffmpeg的标准输入读取
很多时候连续调用ffmpeg时,前一个进程的标准输入没有正确关闭,导致后一个ffmpeg误读了残留的输入内容,打乱了参数解析。你可以在每个ffmpeg命令末尾加上< /dev/null,强制让ffmpeg不从标准输入读取任何内容:ffmpeg -y -i "http://xx/22.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/22.jpg < /dev/null ffmpeg -y -i "http://xx/23.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/23.jpg < /dev/null这个方法通常能解决大部分连续调用的参数干扰问题。
给每个ffmpeg调用添加独立日志排查细节
你可以给每个命令加上-loglevel debug参数,并将日志输出到单独文件,这样能精准看到前几次调用时参数到底哪里出了问题:ffmpeg -y -loglevel debug -i "http://xx/22.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/22.jpg > /tmp/ffmpeg_22.log 2>&1 ffmpeg -y -loglevel debug -i "http://xx/23.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/23.jpg > /tmp/ffmpeg_23.log 2>&1查看
/tmp/ffmpeg_22.log里的详细日志,应该能找到参数被截断或者混入无关内容的具体原因。用子shell隔离每个ffmpeg进程
把每个ffmpeg命令放在子shell(()包裹)里执行,让每个进程都在独立的环境中运行,避免文件描述符或环境变量的冲突:(ffmpeg -y -i "http://xx/22.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/22.jpg) (ffmpeg -y -i "http://xx/23.m3u8" -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/23.jpg)合并为单个ffmpeg命令处理(推荐)
其实ffmpeg支持一次性处理多个输入并生成多个输出,这样不仅效率更高,还能从根源上避免多次调用的冲突问题。你可以用-map参数指定每个输出对应的输入流:#!/bin/bash ffmpeg -y \ -i "http://xx/22.m3u8" \ -i "http://xx/23.m3u8" \ # 处理第一个输入,生成22.jpg -map 0:v -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/22.jpg \ # 处理第二个输入,生成23.jpg -map 1:v -qscale:v 2 -vframes 1 -vf "drawtext=fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf: text='%{localtime}': fontsize=20: fontcolor=white@0.9: x=20: y=20: box=1: boxcolor=red@0.2" /var/xxx/23.jpg这样一次调用就能完成所有任务,也不会出现参数干扰的问题。
内容的提问来源于stack exchange,提问作者Idigest

