调用ffmpeg.c两次会导致应用程序崩溃的问题?

7
我正在尝试调用ffmpeg.c来根据此代码'video-trimmer'修剪视频。当我尝试运行加载和使用本机库的活动并首次单击“trim”时,它可以工作并且我可以修剪视频,但是当我再次尝试运行它时,它会崩溃(只有在应用程序重新启动时才能正常工作)。
因此,我花了三天时间寻找解决此问题的方法,大多数答案都说ffmpeg.c中的静态变量存在问题,并创建一个加载和卸载类的库可以解决该问题(answer1answer2)。因此,我尝试应用基于答案的解决方案和this github repo到video-trimmer项目上,但我的所有尝试都失败了。

有人知道'video-trimmer'项目的一个分支可以解决问题吗?或者有人能够提供实现解决方案的逐步说明,以在'video-trimmer'项目中实现解决方案(因为我尝试跟随网上的所有解决方案并将它们应用于该项目,但没有成功)。


你有代码的快照吗? - Sruit A.Suk
实际上,我只是下载了这个项目并尝试运行它。这里是调用ffmpeg库的地方 https://github.com/uday-rayala/video-trimmer/blob/master/jni/video-trimmer.c。 - Jimmy
请给我们一个简短的示例和说明,以便我们可以重现它。http://sscce.org/。如果您没有编写任何代码,则只需提供逐步说明,以便我们可以在我们的工作站上重现错误。一定要提到平台和操作系统版本号以及相关库的版本号。 - Spundun
请帮我,我该如何解决这个问题。 - koti
我不得不编写自定义的C接口来调用FFMPEG源代码,而不是调用库。我只建议在有很多时间可以浪费的情况下这样做,因为它没有太多的文档可用。 - Jimmy
4个回答

2
问题似乎出在初始化值上(某些变量被声明为全局静态变量,可能是为了方便访问,但这违反了面向对象的原则,会导致像你遇到的问题),然而我能想到几种解决方法:
- 编写一个快速函数来手动将静态变量设置回它们正确的初始值(快速而且有效)。以下是一些不应该随意触发的方法列表: - `avcodec_register_all()`, `avdevice_register_all()`, `av_register_all()` - `avcodec_find_encoder()`, `avcodec_find_decoder()`, `av_find_stream_info()` - `avcodec_open()`, `avcodec_close()` - 可以将它们封装在一个布尔控制方法中,例如,如果它们已经运行过,则不能再次运行。 - 另一种控制方式是通过手动强制变量值(通过使用类或结构体来控制 ffmpeg 全局变量),这些变量在后续运行中被重新初始化。例如,在运行当前导致代码失败的方法时,第一步可以手动将变量设置回其默认设置,以便它们正常运行,因为我怀疑您在迭代之间保留了数据,这就是问题所在。 - 您可以使用互斥锁来确保上述方法在使用线程时更加负责。
附加说明:
- 如果您要多次调用 main(),则应在 C 级别上使用 `libffmpeginvoke` 而不是 `libffmpeg`。 - 在加载 ffmpeg 库时强制执行垃圾回收(是的,这是另一种“丑陋”的解决方法),然后它将清理事物,使您能够调用另一个实例。
如果您需要更深入的内容,请告诉我。我可以尝试创建一个测试框架来复制您的问题并查看结果,但这需要访问我的家用 PC,因为当我在工作时,我没有 Android SDK。

如果所有全局变量都可以找到,强制重新初始化全局变量是一个好主意。至于垃圾回收等方面,你有证据表明Android会从正在运行的进程中卸载共享库吗? - Chris Stratton
我已经简单地使用ffmpeg玩了一下(大约30分钟左右),发现libffmpeginvoke在Android下更加稳定,因为它利用了一个预先存在的实例,而不是libffmpeg尝试初始化一个新实例,这似乎会导致问题,特别是当全局变量已经被修改时。我今天/今晚(GMT)将进一步调查。 - GMasucci
@GMasucci:感谢您的努力,看完后请告诉我结果。我进行了一些测试,但仍然存在问题。 - Jimmy
嗨,Jimmy,如果你愿意的话,我可以看一下任何导致你问题的代码,只要在这里喊一声。我会尝试制作一个包装系统来处理初始化,但这可能是最好的临时解决方案,基于一般原则,更好/更清洁的解决方案可以编码。到目前为止,libffmpeginvoke()已经解决了我使用libffmpeg()时遇到的任何问题。 - GMasucci
1
关于 https://stackoverflow.com/questions/52916376/calling-ffmpeg-cs-main-twice-causes-app-crash - user924
显示剩余7条评论

1

请帮助我们帮助您,请提供您已实施的代码或其中一部分。同时,崩溃日志也会很有帮助。

提示:初始化ffmpeg对象/线程。 然后使用回调接口。一旦VideoTrimmer完成,就给出回调。 在该回调中调用ffmpeg对象/线程的销毁/终止。

也许这个链接可以帮助您。

我最近使用了来自github的"android-ffmpeg-java"项目,这是一个可行的库,我可以保证。您只需实现一个包装器(测试应用程序),它将完成工作。
查看此源链接:android-ffmpeg-java
查看此示例链接:android-ffmpeg-cmdline。看看您是否可以通过此解决问题。


我尝试过了,它不在线程级别。这里是调用ffmpeg库的地方 https://github.com/uday-rayala/video-trimmer/blob/master/jni/video-trimmer.c。 - Jimmy
调用Java_net_video_trimmer_natives_VideoTrimmer_trim方法应该实现一个线程。
CallVideoTrimmer() { //启动线程
//注册回调函数 Java_net_video_trimmer_natives_VideoTrimmer_trim(env, myClass, inputFile, outFile, startTime, length);
//执行回调函数
//结束线程
- Arpan
我尝试了一下,但没有成功。再次注册本地方法时,它似乎不在线程的范围内。 - Jimmy
这个答案并不基于对Android中进程级静态变量问题的理解。在这里详细说明这些问题是不切实际的,尽管可以编写一个小型问题演示库用于测试目的。 - Chris Stratton

0

我不确定这是否有帮助,但是C文件通常有一个头文件,您可以在其中使用

#ifndef

请参见以下内容: http://www.cprogramming.com/reference/preprocessor/ifndef.html

使用该语法将声明夹在相关的.h文件中,以确保多个导入不会导致导入代码崩溃。

祝你好运!

编辑:好的,看起来这意味着需要将ffmpeg重新编译为.so文件。您应该尝试验证它在代码库中是否具有上述机制,并确认它没有被加载两次。


这与问题无关,该问题发生在运行时而非编译时。问题是当在重用的现有进程中运行任务的新实例时 - ffmpeg和许多其他程序没有预料到这种情况,但在Android上非常常见。 - Chris Stratton
@Chris - 好的,当多种语言开始交叉时,我就会感到怀疑。如果代码从C库动态加载超过一次,我真的不知道该预测什么-你呢?无论如何,假设它是从VLAN编译的,应该没问题-http://git.videolan.org/?p=ffmpeg.git;a=blob;f=ffmpeg.h;h=1260563cb8c0bb526628abfc596dc8ad24556ac7;hb=HEAD - DrM
代码不会被加载超过一次,因为进程中的第二个加载调用最终什么也不做。问题在于,从未打算再次运行的代码部分正在重复运行,因为Android保持进程以进行重用。除非vlan的版本已经彻底修订以适应这种独特的Android行为,否则它不会是解决方案。请记住,仅使用ffmpeg编解码器并不等同于使用整个包括将它们串在一起的代码。 - Chris Stratton

0

虽然有些粗糙,但一个潜在的解决方法可能是从服务中利用/链接到ffmpeg(无论如何最好这样做),该服务在清单中声明为在其自己的进程中运行而不是客户端活动的进程中运行。然后,在任务完全完成时,使该进程终止自身-如果需要,调用本机exit()。Android不会特别喜欢发生这种情况-这不是好的实践-但您可能可以使其正常工作。

重新设计库以能够将自身重置为新状态(甚至使其完全上下文化)会更好,但对于庞大的遗留代码库来说可能是一个大项目。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接