ffmpeg Получить время кадров из обрезанного видео

Я использую FFmpeg в своем приложении для извлечения кадров из видео, кадры будут добавлены в вид обрезки видео, где вы получите иллюстрацию того, что происходит в видео в определенное время в видео. Таким образом, каждый кадр должен представлять некоторое время в видео.

Я не совсем понимаю, как FFmpeg создает кадры. Вот мой код:

"-i",
videoCroppedFile.getAbsolutePath(),
"-vf",
"fps=1/" + frameSeperation,
mediaStorageDir.getAbsolutePath() +
"/%d.jpg"

Мое приложение позволяет записывать видео максимальной продолжительностью 20 секунд. Количество кадров, извлеченных из видео, зависит от продолжительности захваченного видео. frameSeperation вычисляется с помощью приведенного ниже кода.

String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
    long videoLength = Long.parseLong(time) / 1000;
    double frameSeperationDouble = (double) videoLength;
    // Divide by 11 because there is a maximum of 11 frames on trim video view
    frameSeperationDouble /= 11;
    frameSeperationDouble = Math.ceil(frameSeperationDouble);
    int frameSeperation = (int) frameSeperationDouble;

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

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

  • Видео, снятое продолжительностью 6 секунд, состоит из 7 кадров.
  • Видео, снятое продолжительностью 2 секунды, состоит из 3 кадров.
  • Видео, снятое продолжительностью 10 секунд, состоит из 12 кадров.
  • Видео, снятое продолжительностью 15 секунд, состоит из 9 кадров.
  • Видео, снятое продолжительностью 20 секунд, состоит из 11 кадров.

Нет согласованности, и из-за этого мне трудно ставить временные метки для каждого кадра. Я чувствую, что моя логика неверна или я не понимаю. Любая помощь высоко ценится

Обновление 1

Итак, я сделал то, что вы сказали в комментариях:

 final FFmpeg ffmpeg = FFmpeg.getInstance(mContext);
        final File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
                + "/Android/data/"
                + mContext.getPackageName()
                + "/vFrames");

    if (!mediaStorageDir.exists()){
        mediaStorageDir.mkdirs();
    }

    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    retriever.setDataSource(mContext, Uri.fromFile(videoCroppedFile));
    String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
    long videoLength = Long.parseLong(time) / 1000;
    double frameSeperationDouble = (double) videoLength / 8;

    retriever.release();

    final String cmd[] = {

            "-i",
            videoCroppedFile.getAbsolutePath(),
            "-vf",
            "fps=1/" + frameSeperationDouble,
            "-vframes," + 8,
            mediaStorageDir.getAbsolutePath() +
            "/%d.jpg"
    };

Я также пробовал "-vframes=" + 8 в тот же момент, когда я помещал vFrames в cmd. Кажется, это вообще не работает, теперь из видео не извлекаются кадры.


person TheOtherguyz4kj    schedule 16.11.2017    source источник
comment
если вы хотите получить каждый кадр видео, просто избавьтесь от -vf fps=1/[frameseparation]. Если вы хотите захватывать изображение каждые x секунд, вам, вероятно, следует использовать -r fps вместо этого.   -  person TheAtomicOption    schedule 16.11.2017


Ответы (1)


Это эффективный поток для fps=x с методом округления по умолчанию near,

Генерировать интервалы 1/x секунд, например. для x=1/3 интервалы равны 0-3 с, 3-6 с, 6-9 с... В каждом интервале выберите более ранний кадр, отметка времени которого находится ближе всего к середине этого интервала. Это может привести к дублированию кадров, когда ближайший кадр принадлежит более раннему интервалу и уже был выбран для этого интервала. Скорее всего, если источник имеет переменную частоту кадров.

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

person Gyan    schedule 16.11.2017
comment
Я смутно понимаю, что вы предлагаете, но не полностью. Не могли бы вы дать мне пример кода, если это возможно? потому что длина видео варьируется, поэтому я не совсем понимаю, как будет работать ваш сценарий. Благодарность - person TheOtherguyz4kj; 16.11.2017
comment
Ваша цель получить 11 кадров из каждого видео, независимо от продолжительности? - person Gyan; 16.11.2017
comment
Честно говоря, 8 кадров — это то количество, которое я хочу, независимо от продолжительности. - person TheOtherguyz4kj; 17.11.2017
comment
Разделите продолжительность видео на 8 и не преобразовывайте в целое число. Оставьте (как минимум 2) десятичных значения и передайте их в ffmpeg. Добавьте "-vframes", "8" после фильтра. - person Gyan; 17.11.2017
comment
Пожалуйста, проверьте обновленный ответ. Я действительно очень ценю вашу помощь, потому что я застрял на этом некоторое время. - person TheOtherguyz4kj; 17.11.2017
comment
Вы можете сделать double frameSeperationDouble = (double) Long.parseLong(time) / 8000;? -vframes, --› запятая должна быть снаружи. Добавить "-vsync", + 0, - person Gyan; 17.11.2017
comment
С точки зрения синтаксиса они оба не имеют смысла. Взгляните на мой скриншот gyazo.com/93fdf4a492d9f5ab5aa89cb17482670c - person TheOtherguyz4kj; 17.11.2017
comment
Я поставил +, потому что вы поставили его в Q --› "-vframes," + 8, - person Gyan; 18.11.2017
comment
Поэтому, если я сделаю это так, как вы сказали, возникнет синтаксическая ошибка gyazo.com/cdd21063e194a88db9c1f3dd6b40619b любая другая предложения? Вы посмотрите на остальные мои команды, есть ли что-то похожее с синтаксисом? И что должен делать vsync? а vFrames говорит, сколько кадров выводить, верно? - person TheOtherguyz4kj; 18.11.2017
comment
Я проверил онлайн, похоже, что vframes в String должны быть чем-то вроде -vframes 8, так что в моем коде это эквивалентно "-vframes " + 8 Однако это также не создает кадров. моя полная команда final String cmd[] = { "-i", videoCroppedFile.getAbsolutePath(), "-vf", "fps=1/" + frameSeperationDouble, "-vframes " + 8, "-vsync "+ 0, mediaStorageDir.getAbsolutePath() + "/%d.jpg" }; - person TheOtherguyz4kj; 18.11.2017