FFmpeg向视频过程添加图像水印非常慢

Tus*_*iya -1 android ffmpeg android-ffmpeg

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

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

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

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

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

HB.*_*HB. 5

您提到 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};
Run Code Online (Sandbox Code Playgroud)

我稍微降低了我的帧率,您可以尝试最适合您的方法。我正在使用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};
Run Code Online (Sandbox Code Playgroud)

编辑

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

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};
Run Code Online (Sandbox Code Playgroud)

这适用于显示纵横比为 的设备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"; 
}
Run Code Online (Sandbox Code Playgroud)

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

接下来,创建一个水印并将其添加到视图中,使该视图成为设备的大小 ( match_parent) 并将水印缩放/放置在您想要的位置。然后您可以通过调用获取位图:

Bitmap waterMarkBitmap = watermarkView.getDrawingCache();
Run Code Online (Sandbox Code Playgroud)

并从 中创建一个文件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(); 
}
Run Code Online (Sandbox Code Playgroud)

水印已创建并可重复使用,或者您可以在完成后将其删除。

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