Android FFmpeg在视频处理中加入图像水印的速度非常慢

Android FFmpeg在视频处理中加入图像水印的速度非常慢,android,ffmpeg,android-ffmpeg,Android,Ffmpeg,Android Ffmpeg,我在FFmpeg的帮助下将图像水印添加到视频中,但是使用下面的命令FFmpeg需要花费大量的时间- String[] cmd = {"-i",videoPath, "-i", waterMark.toString(),"-filter_complex","overlay=5:5","-codec:a", "copy", outputPath}; 因此,我尝试了另一个命令,该命令稍微快一点,但增加了输出文件的大小(我不想要) 有人请向我解释如何在不增加输出大小的情况下提高FFmpeg水印速度。

我在FFmpeg的帮助下将图像水印添加到视频中,但是使用下面的命令FFmpeg需要花费大量的时间-

String[] cmd = {"-i",videoPath, "-i", waterMark.toString(),"-filter_complex","overlay=5:5","-codec:a", "copy", outputPath};
因此,我尝试了另一个命令,该命令稍微快一点,但增加了输出文件的大小(我不想要)

有人请向我解释如何在不增加输出大小的情况下提高FFmpeg水印速度。
谢谢。

您提到7MB视频需要30-60秒

在速度和质量之间进行选择时,总是要进行权衡

我用一个7MB的文件在我的手机上进行了测试,测试耗时13秒,仍然很慢,但我们不能期望比这更好

提高速度的方法:

  • 使用
    -r
    命令降低帧速率
  • 使用
    -b:v
    -b:a
    命令更改比特率
  • 使用
    -crf
    更改恒定速率系数。默认值为
    21
量化器刻度的范围为0-51:,其中0为无损,23为默认值,51为最差值。值越低质量越高,主观正常范围为18-28。考虑18在视觉上是无损的或几乎是这样的:它应该看起来与输入相同或几乎相同,但它在技术上是无损的。 这就是我发现在大多数android设备上最有效的方法:

String[] s = {"-i", VideoPath, "-i", ImagePath, "-filter_complex", "[0:v]pad=iw:if(lte(ih\\,iw)\\,ih\\,2*trunc(iw*16/9/2)):(ow-iw)/2:(oh-ih)/2[v0];[1:v][v0]scale2ref[v1][v0];[v0][v1]overlay=x=(W-w)/2:y=(H-h)/2[v]", "-map", "[v]", "-map", "0:a", "-c:v", "libx264", "-preset", "ultrafast", "-r", myFrameRate, directoryToStore[0] + "/" + SavedVideoName};
我稍微降低了帧速率,你可以试验一下什么对你最合适。我正在使用
mp4parser
检索帧速率

我必须感谢@Gyan,它为我提供了一种完美缩放视频顶部图像的方法,你可以看看我问的问题

如果不确定帧速率,可以将其从命令中删除,并在速度降低时进行第一次测试

试试看,如果你有任何问题,请提问


OP选择使用以下命令:

String[] cmd = {"-y","-i", videoPath, "-i", waterMark.toString(), "-filter_complex", "overlay=(main_w-overlay_w-10):5", "-map", "0:a","-c:v", "libx264", "-crf", "28","-preset", "ultrafast" ,outputPath};

编辑

只需添加我提到的命令,并提供如何使用该命令等的详细说明:

String[] cmd = {"-i", videoPath, "-i", waterMark.toString(), "-filter_complex", "[0:v]pad=iw:if(lte(ih\\,iw)\\,ih\\,2*trunc(iw*16/9/2)):(ow-iw)/2:(oh-ih)/2[v0];[1:v][v0]scale2ref[v1][v0];[v0][v1]overlay=x=(W-w)/2:y=(H-h)/2[v]", "-map", "[v]", "-map", "0:a", "-c:v", "libx264", "-preset", "ultrafast", "-r", myFrameRate, outputPath};
这适用于显示纵横比为
16:9
的设备。如果希望此过滤器在所有设备上工作,则必须获得设备的纵横比,并分别更改过滤器
16/9/2

通过创建以下方法,可以获得设备纵横比:

int gcd(int p, int q) { 
    if (q == 0) return p; 
    else return gcd(q, p % q); 
} 
void ratio(int a, int b) { 
    final int gcd = gcd(a,b); 
    if(a > b) { 
        setAspectRatio(a/gcd, b/gcd); 
    } else { 
        setAspectRatio(b/gcd, a/gcd); 
    } 
} 
void setAspectRatio(int a, int b) { 
    System.out.println("aspect ratio = "+a + " " + b); 
    //This is the string that will be used in the filter (instead of hardcoding 16/9/2
    filterAspectRatio = a + "/" + b + "/" + "2"; 
}
现在您有了正确的纵横比,可以相应地更改过滤器

接下来,创建一个水印并将其添加到视图中,使该视图与设备的大小相匹配(
匹配\u parent
),并缩放/放置水印。然后,您可以通过调用以下命令获取位图:

Bitmap waterMarkBitmap = watermarkView.getDrawingCache();
并从
位图创建一个文件,如下所示:

String outputFilename = "myCoolWatermark.png"; //provide a name for you saved watermark
File path = Environment.getExternalStorageDirectory(); //this can be changed to where you want to store the bitmap
File waterMark = new File(path, outputFilename); 

try (FileOutputStream out = new FileOutputStream(waterMark)) { 
    waterMarkBitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // PNG is a lossless format, the compression factor (100) is ignored 
} catch (IOException e) { 
    e.printStackTrace(); 
}
水印已创建,可以重复使用,也可以在使用完毕后将其删除


现在您可以调用上面提到的命令。

这是一个非常常见的问题简单的答案是,您无法在Android上提高
ffmpeg
的编码速度。
您是在手机上编码的,因此您不希望使用编码器和硬件加速支持来提高桌面/服务器的性能

用户可以做以下几件事:

  • 带有
    -c:a副本的音频(您已经在这样做了)

  • 使用
    -preset ultrafast
    放弃编码效率以提高编码速度(您也已经这样做了)

  • 使用过滤器使输出宽度x高度变小(可能不是您可以接受的选项)

  • 确保您的x264未使用
    --disable asm
    编译,这样您就可以利用x264中的各种ARM和NEON优化显著提高编码速度。然而,我不知道哪些安卓设备支持这一点,但这是值得研究的。要快速查看是否正在使用任何优化,请参阅
    ffmpeg
    中的控制台输出,并使用cpu功能搜索
    。如果
    那么它就不使用任何优化,否则它可能会说
    ARMv7-NEON
    或者类似的话

  • 将编码卸载到服务器。还可以节省用户的电池寿命

  • 所有这些都是为了一个恼人的水印?避免重新编码,并使用播放器覆盖水印

  • 显然,编码是这里的瓶颈。不过,它可能会节省一些fps

  • 向FFmpeg发送一个支持MediaCodec编码的补丁,或者等待几年,等待其他人这样做

  • 忘记ffmpeg
,直接使用MediaCodec。我对此一无所知,懒得去查找它,但我假设它使用硬件来编码,我猜你可以用它来制作一个覆盖。如果我错了,有人纠正我


输入视频的大小(分辨率/字节)是多少?您在哪个设备上运行?你认为什么是“非常慢”?我不知道您是否意识到了这一点,但添加水印需要重新编码整个视频流(逐帧),因此速度不太快。@Pawel视频大小小于7mb。在摩托G3上跑步。向视频添加水印的速度很慢。@Pawel请看这里[。如果你有任何想法,请告诉我。tnx。你说慢,使用7MB视频需要多长时间?@HB。这需要30秒到60秒。请让我知道如何在这里找到我的帧速率?@Tushallathia这里有一个链接解释如何获取帧速率-FFmpeg思想运行时错误:[AVFilterGraph@0xb87b2da0]没有这样的过滤器:“iw”是一个很好的教程。+1回答很好。我还想提到mediacodec,它速度快得多,但复杂得多。我用相同的视频(7MB)进行了测试使用mediacodec将时间从13秒减少到8秒。您的最后一点是正确的。@HB.请详细解释您使用mediacodec的解决方案。如果可能,请共享您的工作代码。我已经尝试过[但是]
String outputFilename = "myCoolWatermark.png"; //provide a name for you saved watermark
File path = Environment.getExternalStorageDirectory(); //this can be changed to where you want to store the bitmap
File waterMark = new File(path, outputFilename); 

try (FileOutputStream out = new FileOutputStream(waterMark)) { 
    waterMarkBitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // PNG is a lossless format, the compression factor (100) is ignored 
} catch (IOException e) { 
    e.printStackTrace(); 
}