В данной статье описывается возможность создания превью видео файла на сервере и дальнейшее отображение в Android.
Получение превью
Видео можно взять из камеры android, но здесь будет показан пример когда файл уже есть на сервере и его нужно обработать.
Допустим мы получили некий файл video.mp4
Для дальнейшей работы с видео нам потребуется ffmpeg. Переходим по ссылке и скачиваем требуемый дистрибутив.
Примечание: Манипуляции с ffmpeg будут осуществляться в операционной системе Windows 10.
Далее нам нужно сделать так чтобы команда ffmpeg была доступна из командной строки. Для этого нужно каталог с бинарными файлами добавить в переменную среду.
Далее нам нужно с ним провести следующие манипуляции:
- узнать размер кадра
Для этого нам понадобиться команда ffprobe — она позволяет узнать информацию о видео файле. На требуется в этой информации найти видео поток.
Из полученной информации мы понимаем, размер кадра у видео равен
width=1280 height=720
- количество фрагментов
По аналогии с определение размера кадра можно узнать и количество фрагментов. Данная информация храниться в параметре nb_frames и она равна 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; }