FFmpeg给视频添加图片水印过程很慢

FFmpeg adding image watermark to video process is very slow

我在 FFmpeg 的帮助下向视频添加图像水印,但 FFmpeg 使用以下命令花费了过多的时间-

String[] cmd = {"-i",videoPath, "-i", waterMark.toString(),"-filter_complex","overlay=5:5","-codec:a", "copy", outputPath};

所以我尝试了另一个命令,它稍微快一点但增加了输出文件大小(我不想要)

String[] cmd = {"-y","-i", videoPath, "-i", waterMark.toString(), "-filter_complex", "overlay=5:5", "-c:v","libx264","-preset", "ultrafast", outputPath};

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

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

在速度和质量之间做出选择时总是需要权衡取舍。

我在 phone 上使用一个 7MB 的文件进行了测试,它花了 13 秒,仍然很慢,但我们不能期望比这更好。

提高速度的方法:

  • 降低帧率,使用-r命令
  • 更改比特率,使用 -b:v-b:a 命令
  • 使用 -crf 更改恒定速率因子。默认值为 21

The range of the quantizer scale is 0-51: where 0 is lossless, 23 is default, and 51 is worst possible. A lower value is a higher quality and a subjectively sane range is 18-28. Consider 18 to be visually lossless or nearly so: it should look the same or nearly the same as the input but it isn't technically lossless.

这是我发现在大多数 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"; 
}

现在您有了正确的纵横比,您可以相应地更改滤镜。

接下来,创建一个水印并将其添加到视图中,使该视图大小与设备相同 (match_parent),并且 scale/place 水印位于您希望的位置。然后您可以通过调用获取位图:

Bitmap waterMarkBitmap = watermarkView.getDrawingCache();

并从 Bitmap 创建一个文件,如下所示:

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 的编码速度。 您正在 phone 上编码,所以您不要指望 desktop/server 使用编码器和不支持硬件加速的性能。

用户可以做几件事:

  • Stream copy 带有 -c:a copy 的音频(您已经这样做了)。

  • -preset ultrafast为了编码速度放弃编码效率(你也已经这样做了)

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

  • 确保您的 x264 未使用 --disable-asm 编译,以便您可以利用 x264 中的各种 ARM 和 NEON 优化来显着提高编码速度。但是,我不知道哪些 Android 设备支持该功能,但值得研究。要快速检查您是否正在使用任何优化,请参考 ffmpeg 的控制台输出并搜索 using cpu capabilities。如果 none! 那么它没有使用任何优化,否则它可能会说 ARMv7 NEON 或类似的东西。

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

  • 这一切都是为了一个恼人的水印?避免重新编码,使用播放器叠加水印。

  • 显然FFmpeg has MediaCodec decoding support on Android,但编码是这里的瓶颈。但是也许它会节省几帧。

  • 向 FFmpeg 发送补丁以启用 MediaCodec 编码支持,或者等待几年让其他人这样做。

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