MediaMetadataRetriever.getFrameAtTime() возвращает только первый кадр

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

Как мне решить эту проблему?

Ответы

Ответ 1

MediaMetadataRetriever getFrameAt метод принимает в микросекундах (1/1000000 часть секунды) вместо миллисекунд, поэтому в вашем случае он всегда округляется до первого кадра.

Ответ 2

Просто преобразуйте миллисекунды в микро, потому что 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);
        }
    }
}

Ответ 3

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) {


            }

Ответ 5

Я предлагаю вам использовать FFmpegMediaMetadataRetriever. Вы можете получить доступ к проекту здесь. Когда я программировал приложение для своей диссертации, я несколько раз пытался использовать стандартную библиотеку. Это хорошо, но когда дело доходит до извлечения фреймов, у него может быть несколько проблем, даже если вы используете его правильно.

Ответ 6

@Навнеет Кришна

Видео имеют ключевые кадры (иногда называемые кадрами синхронизации). Проблема может заключаться в следующем: первый кадр является ближайшим синхронизирующим кадром при использовании 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 ();

Ответ 7

Попробуйте это для вашего цикла/цикла:

Bitmap bitmap = retriever.getFrameAtTime(
    TimeUnit.MICROSECONDS.convert(i, TimeUnit.MILLISECONDS), 
    retriever.OPTION_CLOSEST_SYNC);