Вывод превью видео файла после обработки на сервере

В данной статье описывается возможность создания превью видео файла на сервере и дальнейшее отображение в Android.

Получение превью

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

Допустим мы получили некий файл video.mp4

Для дальнейшей работы с видео нам потребуется ffmpeg. Переходим по ссылке и скачиваем требуемый дистрибутив.

Примечание: Манипуляции с ffmpeg будут осуществляться в операционной системе Windows 10.

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

каталог с бинарными файлами
Выбираем Path
Добавляем каталог
После этого все команды будут доступны через командную строку

Далее нам нужно с ним провести следующие манипуляции:

  • узнать размер кадра

Для этого нам понадобиться команда ffprobe — она позволяет узнать информацию о видео файле. На требуется в этой информации найти видео поток.

Информация о видео потоке обычно храниться в первом «блоке» [STREAM]

Из полученной информации мы понимаем, размер кадра у видео равен

width=1280
height=720
  • количество фрагментов

По аналогии с определение размера кадра можно узнать и количество фрагментов. Данная информация храниться в параметре nb_frames и она равна 739

количество кадров равно 739
  • выполнить процедуру преобразования

Для запуска преобразования видео в изображение нам нужно выполнить следующую команду:

ffmpeg -loglevel panic -y -i "video.mp4" -frames 1 -q:v 1 -vf "select=not(mod(n\,7)),scale=-1:120,tile=100x1" video_preview.jpg

, где:

  • loglevel panic — вывод логов, отображается только основная информация
  • «video.mp4» — путь к входному файлу
  • frames 1 — требуется достать только один фрагмент за одну выборку
  • q:v — качество выходных данных, 0 — наилучшее качество
  • vf select — выборка кадров
    • not(mod(n\,7)) — получаем каждый 7 кадр
    • scale=-1:120 — указываем что высота выбранного кадра должна быть 120 пикселей
    • title=100×1 — количество фрагментов в результат, тут 100
  • video_preview.jpg — результат обработки

Примечание: Откуда числа 7 и 100? Все просто, 100 — это число которое мы сами решаем указать, сколько кадров мы хотим видеть в результате, а вот 7 вычисляется по следующей формуле

nb_frames / 100 т.е. 739 / 100 = ~7

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

Пример сформированного файла

Далее указанный файл нам нужно вывести в Android.

Вывод превью от ffmpeg в Android

Исходный код программы содержится тут

git clone git://git.appcode.pw/ffmpeg-video-preview-android.git

Основные моменты:

  • созданное превью помещается в папку res/raw
  • activity_main.xml содержит следующий код
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:gravity="center">

    <ImageView
        android:id="@+id/ivPreview"
        android:layout_width="213dp"
        android:layout_height="120dp" />

</LinearLayout>

Тут в файле один контейнер в котором есть ImageView

Примечание: размеры контейнера получаются следующим образом: высота берется из файла video_preview.jpg, вот ширина вычисляется по следующей формуле:

(video width * preview height) / video height
Результатом будет ~213 
  • MainActivity содержит следующий код:
String TAG = "LOG";
int width = 213;
int height = 120;
int maxWidth = 0;
int x = 0;

InputStream inputStream = null;
ImageView ivPreview = null;
Bitmap bmp = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    inputStream = getResources().openRawResource(R.raw.video_preview);
    bmp = BitmapFactory.decodeStream(inputStream);
    maxWidth = bmp.getWidth();
    ivPreview = findViewById(R.id.ivPreview);

    Timer timer = new Timer();
    timer.schedule(new UpdateTimeTask(), 0, 41);
    Log.d(TAG, "main thread: " + Thread.currentThread().getId());
}

class UpdateTimeTask extends TimerTask {
    public void run() {
        Log.d(TAG, "timer thread: " + Thread.currentThread().getId());
        try {
            final Bitmap resizedbitmap = Bitmap.createBitmap(bmp, x, 0, width, height);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ivPreview.setImageBitmap(resizedbitmap);
                }
            });

            x += width;
            if (x >= maxWidth) {
                x = 0;
            }
        }catch (IllegalArgumentException e){
            Log.d(TAG, "ошибка");
        }
    }
}

Основные моменты здесь следующие:

  • наше превью нужно преобразовать в Bitmap
inputStream=getResources().openRawResource(R.raw.video_preview);
Bitmap bmp = BitmapFactory.decodeStream(inputStream);
  • узнать длину изображения
maxWidth = bmp.getWidth();
// это нам нужно для определения конца кадров
  • создаем свой таймер, который должен выводить в нашем ImageView Bitmap

Примечание: интервал времени можно выбрать любой. Я выбирал из условия 24 кадра в секунду.

  • в обработке таймера просто вырезаем требуемое изображение из оригинального Bitmap
final Bitmap resizedbitmap = Bitmap.createBitmap(bmp, x, 0, width, height);
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        ivPreview.setImageBitmap(resizedbitmap);
    }
});
  • и сохраняем позицию x с которого нужно получить следующий кадр. И так делаем до тех пор пока не достигнем максимальной длины, после чего просто сбрасываем текущее положение на 0.
x += width;
if (x >= maxWidth) {
    x = 0;
}

Дополнительные статьи

Print Friendly, PDF & Email

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *