Ответ 1
MediaMetadataRetriever
getFrameAt
метод принимает в микросекундах (1/1000000 часть секунды) вместо миллисекунд, поэтому в вашем случае он всегда округляется до первого кадра.
Я извлечил кадры из видео с помощью MetadataRetriever и сохранил все изображения в ArrayList<Bitmap>
. Я хочу сохранить их все на SD-карте (только для целей тестирования).
Но когда я вытаскиваю папку из эмулятора и смотрю на сохраненные изображения, все изображения были только для первого кадра видео.
Вот как я извлекаю кадры из видео:
File videoFile=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/videos","sample_mpeg4.mp4");
Uri videoFileUri=Uri.parse(videoFile.toString());
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(videoFile.getAbsolutePath());
ArrayList<Bitmap> rev=new ArrayList<Bitmap>();
//Create a new Media Player
MediaPlayer mp = MediaPlayer.create(getBaseContext(), videoFileUri);
int millis = mp.getDuration();
for(int i=0;i<millis;i+=100){
Bitmap bitmap=retriever.getFrameAtTime(i,OPTION_CLOSEST_SYNC);
rev.add(bitmap);
}
И вот как я их сохраняю (я вызываю этот метод и передаю bitmap ArrayList):
public void saveFrames(ArrayList<Bitmap> saveBitmapList) throws IOException{
Random r = new Random();
int folder_id = r.nextInt(1000) + 1;
String folder = Environment.getExternalStorageDirectory()+"/videos/frames/"+folder_id+"/";
File saveFolder=new File(folder);
if(!saveFolder.exists()){
saveFolder.mkdirs();
}
int i=1;
for (Bitmap b : saveBitmapList){
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.JPEG, 40, bytes);
File f = new File(saveFolder,("frame"+i+".jpg"));
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.flush();
fo.close();
i++;
}
Toast.makeText(getApplicationContext(),"Folder id : "+folder_id, Toast.LENGTH_LONG).show();
}
Когда я вытаскиваю папку для просмотра всех фреймов, все изображения имеют первый кадр видео. Может кто-нибудь объяснить мне, что происходит?
UPDATE:
Я пробовал с другим видео. Я обнаружил, что я не получаю пустые изображения, но всегда возвращает только первый кадр.
MediaMetadataRetriever.getFrameAtTime(long timeUS)
возвращает только первый кадр, но я хочу получить все кадры. Какие изменения я должен сделать?
Как мне решить эту проблему?
MediaMetadataRetriever
getFrameAt
метод принимает в микросекундах (1/1000000 часть секунды) вместо миллисекунд, поэтому в вашем случае он всегда округляется до первого кадра.
Просто преобразуйте миллисекунды в микро, потому что getFrameAt получает данные в миллисекундах
1 miliseconds have 1000 microseconds..
for(int i=1000000;i<millis*1000;i+=1000000){
Bitmap bitmap=retriever.getFrameAtTime(i,OPTION_CLOSEST_SYNC);
rev.add(bitmap);
}
тогда ваша проблема решена.
Я создал его в соответствии с моей потребностью.
public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File videoFile=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/screenshots/","myvideo.mp4");
Uri videoFileUri=Uri.parse(videoFile.toString());
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(videoFile.getAbsolutePath());
ArrayList<Bitmap> rev=new ArrayList<Bitmap>();
//Create a new Media Player
MediaPlayer mp = MediaPlayer.create(getBaseContext(), videoFileUri);
int millis = mp.getDuration();
for(int i=1000000;i<millis*1000;i+=1000000)
{
Bitmap bitmap=retriever.getFrameAtTime(i,MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
rev.add(bitmap);
}
}
}
long time;
String formattedFileCount;
FileOutputStream fos;
BufferedOutputStream bos;
NumberFormat fileCountFormatter = new DecimalFormat("00000");
int fileCount = 0;
File jpegFile;
ArrayList<Bitmap> bArray = null;
Bitmap lastbitmap = null;
bArray = new ArrayList<Bitmap>();
Я беру время в микросекундах, и они получают продолжительность от медиапланера, например:
time = mp.getDuration()*1000;
Log.e("Timeeeeeeee", time);
bArray.clear();
MediaMetadataRetriever mRetriever = new MediaMetadataRetriever();
mRetriever.setDataSource(path);
int j=0;
Моя частота кадров составляет 12 кадров/сек, поэтому я разделил 1/12 = 0.083333, и это вторая часть кадра, и я конвертирую свой кадр в микросекунду, чтобы она стала 83333
for (int i = 833333; i <= time; i=i+83333) {
bArray.add(mRetriever.getFrameAtTime(i, MediaMetadataRetriever.OPTION_CLOSEST_SYNC));
formattedFileCount = fileCountFormatter.format(fileCount);
lastbitmap = bArray.get(j);
j++;
// image is the bitmap
jpegFile = new File(Environment.getExternalStorageDirectory().getPath() + "/frames/frame_" + formattedFileCount + ".jpg");
fileCount++;
try {
fos = new FileOutputStream(jpegFile);
bos = new BufferedOutputStream(fos);
lastbitmap.compress(Bitmap.CompressFormat.JPEG, 15, bos);
bos.flush();
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
}
Используя MetaMediaRetriever, я смог получить видеоролик видеоролика
Я предлагаю вам использовать FFmpegMediaMetadataRetriever. Вы можете получить доступ к проекту здесь. Когда я программировал приложение для своей диссертации, я несколько раз пытался использовать стандартную библиотеку. Это хорошо, но когда дело доходит до извлечения фреймов, у него может быть несколько проблем, даже если вы используете его правильно.
@Навнеет Кришна
Видео имеют ключевые кадры (иногда называемые кадрами синхронизации). Проблема может заключаться в следующем: первый кадр является ближайшим синхронизирующим кадром при использовании OPTION_CLOSEST_SYNC
...
Попробуйте заменить эту строку:
Bitmap bitmap=retriever.getFrameAtTime(i,OPTION_CLOSEST_SYNC);
с этим (который получает ближайший доступный кадр к данному времени):
Bitmap bitmap=retriever.getFrameAtTime(i,OPTION_CLOSEST);
Читайте о: CLOSEST
& CLOSEST_SYNC
.
Приведенный ниже код не проверен, но представляет собой общее представление о том, "как…", поэтому позвольте мне сейчас, если это поможет вам...
Результат должен составлять одну картинку в секунду продолжительности видео. Тест с коротким видео.
//Create a new Media Player
MediaPlayer mp = MediaPlayer.create(getBaseContext(), videoFileUri);
int millis = mp.getDuration();
for (int i = 0; i < millis; i += 1000)
{
Bitmap bmp = retriever.getFrameAtTime( i * 1000, MediaMetadataRetriever.OPTION_CLOSEST );
if (bmp != null) { rev.add( bmp ); }
}
retriever.release ();
Попробуйте это для вашего цикла/цикла:
Bitmap bitmap = retriever.getFrameAtTime(
TimeUnit.MICROSECONDS.convert(i, TimeUnit.MILLISECONDS),
retriever.OPTION_CLOSEST_SYNC);