【问题标题】:Issue when calling ffmpeg.c twice that makes the app crashes?两次调用 ffmpeg.c 导致应用崩溃的问题?
【发布时间】:2012-12-12 13:38:30
【问题描述】:

我正在尝试调用 ffmpeg.c 以根据此代码 'video-trimmer' 修剪视频。因此,当我第一次单击 trin 尝试运行活动(加载并使用本机库)时,它可以工作并且我可以修剪视频,但是当我尝试再次运行它时它会崩溃(并且它仅适用于应用程序重新启动)。

所以我花了三天时间寻找这个问题的解决方案,大多数答案都说 ffmpeg.c 中的静态变量存在问题,并创建一个加载和卸载类的库解决了这个问题(answer1,@ 987654323@)。因此,我尝试在视频修剪器项目上应用基于答案和this github repo 的解决方案,但我所有的尝试都失败了。

有没有人知道解决问题的'video-trimmer' 项目的一个分支?或者任何人都可以提供如何在'video-trimmer' 项目中实施解决方案的逐步答案(因为我试图遵循网络上的所有解决方案并将它们应用于该项目但没有运气)。

【问题讨论】:

  • 你有你的代码快照吗?
  • 其实我只是下载了项目并尝试运行它。这是 ffmpeg 库被称为 github.com/uday-rayala/video-trimmer/blob/master/jni/… 的地方。
  • 给我们一个简短的例子和指导,以便我们可以重现它。 sscce.org 。如果您没有编写任何代码,那么只需逐步说明,以便我们可以在我们的工作站上重现错误。请务必提及操作系统和相关库的平台和版本号。
  • 请帮我解决这个问题
  • 我不得不编写自定义 C 接口来调用 FFMPEG 源代码而不是调用 lib,我只建议您在有很多时间消磨时间的情况下这样做,因为没有很多可用的文档.

标签: android c android-ndk ffmpeg


【解决方案1】:

问题似乎出在初始化值上(一些变量被声明为全局静态变量,大概是为了便于访问,但违反了 OOP 原则并导致我们遇到您所面临的问题),但是,有几种方法可以解决这是我能想到的:

  • 编写一个快速函数来手动将静态变量设置回正确的初始值(快速而肮脏但有效)。不应该被允许触发的方法列表如下:
    • 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实际上会从正在运行的进程中卸载共享库?
  • 克里斯,你说得很好:当我回到家时,我尝试了几次测试并得到了一些意想不到的结果。
  • - 垃圾收集并不总是释放 ram,即使这样做了,它显然也不会总是卸载未使用的库(这是 android 中的一个功能,因为它允许快速重用以前使用的库) - dlclose() 甚至不保证共享库的释放因此我将不得不修改自己以推荐一种方法来使用互斥锁锁定方法或通过方法手动控制问题变量。
  • 我用 ffmpeg 玩了大约(30 分钟左右),发现 libffmpeginvoke 在 android 下工作更稳定,因为它利用预先存在的实例而不是 libffmpeg 尝试初始化一个新实例,这与已经改变的全局变量似乎会导致问题。我今天/今晚(格林威治标准时间)将进一步调查。
【解决方案2】:

帮助我们帮助您,请提供您实现的代码或其中的一部分。崩溃日志也会有所帮助。

提示:初始化 ffmpeg 对象/线程。 然后使用回调接口。一旦 VideoTrimmer 结束,请进行回调。 在该回调中调用 ffmpeg 对象/线程的销毁/杀死。

也许这个link可以帮助你。

我最近使用了来自 github 的“android-ffmpeg-java”项目,这是一个工作库,我可以保证。您只需要实现一个包装器(测试应用程序)就可以完成这项工作。
检查此链接的来源:android-ffmpeg-java
检查此链接,例如:android-ffmpeg-cmdline。看看你能不能解决这个问题。

【讨论】:

  • 我试过了,它不在线程级别。这是 ffmpeg 库被称为 github.com/uday-rayala/video-trimmer/blob/master/jni/… 的地方。
  • 调用 Java_net_video_trimmer_natives_VideoTrimmer_trim 的方法应该实现一个线程。 CallVideoTrimmer() {//start_thread//register callbackJava_net_video_trimmer_natives_VideoTrimmer_trim(env, myClass, inputFile, outFile, startTime, length);//give callback//end thread
  • 我试了一下,没有成功。同样,当您注册本机方法时,它看起来不像它在线程范围内。
  • 此答案并非基于对 Android 中进程级静态变量问题的理解。尽管可以编写一个小型问题演示库用于测试目的,但在这里发布这些范围是不切实际的。
【解决方案3】:

我不确定这是否有帮助,但 C 文件通常有一个可以使用的标题

ifndef

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

使用该语法对相关 .h 文件中的声明进行沙化处理,以确保多次导入不会导致导入代码崩溃。

祝你好运!

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

【讨论】:

  • 这与问题无关,发生在运行时而不是编译时。问题在于,当任务的新实例在重复使用的现有进程中运行时 - 进程 ffmpeg 和许多其他程序不是在预期中编写的,但在 Android 上很常见。
  • @Chris - 好吧,当多种语言开始出现交叉时,我就开始怀疑了。我真的不知道如何预测代码是否从 C 库中多次动态加载 - 你呢?无论如何假设它是从 VLAN 编译的,它应该是好的 - git.videolan.org/…
  • 代码不会被多次加载,因为进程中的第二次加载调用最终什么都不做。问题在于,由于 Android 保留该过程以供重复使用,因此从未打算第二次运行以执行新工作的部分代码正在重复运行。除非 vlan 的版本已经过彻底修改,以适应这种独特的 Android 行为,否则出现问题的假设时,它不会成为解决方案。请记住,仅使用 ffmpeg 编解码器与使用整个东西(包括将它们串在一起的代码)不同。
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多