FFmpeg очень медленно добавляет изображение водяного знака в видео - PullRequest
0 голосов
/ 09 апреля 2019

Я добавляю водяные знаки изображения в видео с помощью 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 скорость нанесения водяных знаков без увеличения размера вывода.Спасибо.

Ответы [ 2 ]

4 голосов
/ 09 апреля 2019

Вы упомянули, что видео размером 7 МБ занимает от 30 до 60 секунд.

При выборе скорости и качества всегда есть компромисс.

Я тестировал на своем телефоне, используя файл размером 7 МБ, и это заняло 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", "-g", "2", "-c:v", "libx264", "-preset", "ultrafast", "-r", myFrameRate, directoryToStore[0] + "/" + SavedVideoName};

Я немного уменьшил частоту смены кадров, вы можете поэкспериментировать, что вам больше подходит. Я использую Google mp4parser для получения частоты кадров.

Я должен отдать должное @Gyan, который предоставил мне способ идеально масштабировать изображения, помещаемые поверх видео, вы можете посмотреть на вопрос, который я задал здесь .

Также вы можете удалить команду -g, которую я использовал выше, для изменения GOP.

Вот ваша команда отредактирована:

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};

Если вы не уверены в частоте кадров, вы можете удалить ее из команды и сначала проверить, не снижена ли ваша скорость.

Попробуйте, если у вас есть какие-либо вопросы, пожалуйста, задавайте.


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) и масштабируйте / разместите водяной знак там, где вы хотите, чтобы он был. Затем вы можете получить растровое изображение, вызвав:

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(); 
}

Водяной знак создан и может быть использован повторно, или вы можете удалить его, когда закончите с ним.

Теперь вы можете вызвать указанную выше команду.

1 голос
/ 09 апреля 2019

Это очень распространенный вопрос здесь. Простой ответ заключается в том, что вы не можете увеличить скорость кодирования ffmpeg на Android. Вы кодируете на телефоне, поэтому вы не ожидаете, что производительность настольного компьютера или сервера при использовании кодера и без аппаратного обеспечения поддержка ускорения.

Пользователь может сделать несколько вещей:

  • Потоковое копирование аудио с -c:a copy (вы уже делаете это).

  • Используйте -preset ultrafast, чтобы снизить эффективность кодирования для скорости кодирования (вы тоже это делаете).

  • Уменьшите ширину вывода x высоту с помощью фильтра scale (вероятно, не приемлемый вариант для вас).

  • Убедитесь, что ваш x264 не скомпилирован с --disable-asm, чтобы вы могли воспользоваться различными оптимизациями ARM и NEON в x264 для значительного увеличения скорости кодирования. Тем не менее, я не знаю, какие устройства Android поддерживают это, но на это стоит обратить внимание. Чтобы быстро проверить, используете ли вы какие-либо оптимизации, обратитесь к выводу консоли из ffmpeg и выполните поиск using cpu capabilities. Если none!, тогда он не использует никаких оптимизаций, в противном случае он может сказать ARMv7 NEON или что-то в этом роде.

  • Снять кодировку с сервера. Экономит заряд батареи ваших пользователей.

  • Все это для раздражающего водяного знака? Избегайте перекодирования и используйте проигрыватель для наложения водяного знака.

  • Видимо FFmpeg имеет поддержку декодирования MediaCodec на Android , но кодирование здесь является узким местом. Однако, возможно, это сэкономит несколько кадров в секунду.

  • Отправьте исправление в FFmpeg, которое включает поддержку кодирования MediaCodec, или подождите несколько лет, пока кто-нибудь другой сделает это.

  • Забудьте ffmpeg и используйте MediaCodec напрямую. Я ничего не понимаю об этом и слишком ленив, чтобы искать его, но я предполагаю, что он использует аппаратное обеспечение для кодирования, и я думаю, вы можете использовать его для наложения. Кто-то поправит меня, если я ошибаюсь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...