Search code examples
androidperformancebitmapframes

Error in retrieving multiple frames of a video via FFmpegMediaMetadataRetriever


I am trying to fetch multiple frames from a video but it sometimes gives fatal exception. What I am doing here is I am fetching frame for a time and rotating if orientation is portrait. Reason of failure might be memory issue. Please suggest where I am wrong.

private ArrayList<Bitmap> getFrames() {

    ArrayList<Bitmap> bmFrames = new ArrayList<>();
    Bitmap bmp = null;
    Bitmap orientedBitmap = null;

    try {
        int baseTime = Integer.parseInt(model.getDuration());
        baseTime = baseTime / 11 * 1000;

        baseTimeArray = new int[11];
        for (int i = 0; i < 11; i++) {
            baseTimeArray[i] = baseTime * (i);

            Log.d("TAG", "fetching frame from " + baseTimeArray[i]);

            try {
                FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
                mmr.setDataSource(new File(sourceFilePath).getAbsolutePath());
                bmp = getBitmapViaFFMPEG(baseTimeArray[i], mmr);

                if (rotationMetaData != null) {
                    Matrix matrix = new Matrix();

                    matrix.postRotate(90);

                    //Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp,50,50,true);
                    if (bmp != null)
                        orientedBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
                    else
                        Log.d(TAG,"returned bitmap was null");
                }
                //Log.d("TAG", "rotation: " + rotation);
                mmr.release();
            } catch (IllegalArgumentException ex) {
                ex.printStackTrace();
            } catch (RuntimeException ex) {
                ex.printStackTrace();
            } catch (Exception ex) {
                ex.printStackTrace();
            }

            if (rotationMetaData != null)
                bmFrames.add(orientedBitmap);
            else
                bmFrames.add(bmp);


        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return bmFrames;
}

Error log

enter image description here


Solution

  • I know it's too much late to post an answer but still it may help someone. Below class can be used to show the video frames

    public class TimeLineView extends View {
    
    private Uri mVideoUri;
    private int mHeightView;
    private LongSparseArray<Bitmap> mBitmapList = null;
    
    public TimeLineView(@NonNull Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public TimeLineView(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    
    private void init() {
        mHeightView = getContext().getResources().getDimensionPixelOffset(R.dimen.frames_video_height);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int minW = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
        int w = resolveSizeAndState(minW, widthMeasureSpec, 1);
    
        final int minH = getPaddingBottom() + getPaddingTop() + mHeightView;
        int h = resolveSizeAndState(minH, heightMeasureSpec, 1);
    
        setMeasuredDimension(w, h);
    }
    
    @Override
    protected void onSizeChanged(final int w, int h, final int oldW, int oldH) {
        super.onSizeChanged(w, h, oldW, oldH);
    
        if (w != oldW) {
            getBitmap(w);
        }
    }
    
    private void getBitmap(final int viewWidth) {
        BackgroundExecutor.execute(new BackgroundExecutor.Task("", 0L, "") {
                                       @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
                                       @Override
                                       public void execute() {
                                           try {
                                               LongSparseArray<Bitmap> thumbnailList = new LongSparseArray<>();
    
                                               MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
                                               mediaMetadataRetriever.setDataSource(getContext(), mVideoUri);
    
                                               // Retrieve media data
                                               long videoLengthInMs = Integer.parseInt(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)) * 1000;
    
                                               // Set thumbnail properties (Thumbs are squares)
                                               final int thumbWidth = mHeightView;
                                               final int thumbHeight = mHeightView;
    
                                               int numThumbs = (int) Math.ceil(((float) viewWidth) / thumbWidth);
    
                                               final long interval = videoLengthInMs / numThumbs;
    
                                               for (int i = 0; i < numThumbs; ++i) {
                                                   Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(i * interval, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
                                                   // TODO: bitmap might be null here, hence throwing NullPointerException. You were right
                                                   try {
                                                       bitmap = Bitmap.createScaledBitmap(bitmap, thumbWidth, thumbHeight, false);
                                                   } catch (Exception e) {
                                                       e.printStackTrace();
                                                   }
                                                   thumbnailList.put(i, bitmap);
                                               }
    
                                               mediaMetadataRetriever.release();
                                               returnBitmaps(thumbnailList);
                                           } catch (final Throwable e) {
                                               Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                                           }
                                       }
                                   }
        );
    }
    
    private void returnBitmaps(final LongSparseArray<Bitmap> thumbnailList) {
        UiThreadExecutor.runTask("", new Runnable() {
                    @Override
                    public void run() {
                        mBitmapList = thumbnailList;
                        invalidate();
                    }
                }
                , 0L);
    }
    
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        super.onDraw(canvas);
    
        if (mBitmapList != null) {
            canvas.save();
            int x = 0;
    
            for (int i = 0; i < mBitmapList.size(); i++) {
                Bitmap bitmap = mBitmapList.get(i);
    
                if (bitmap != null) {
                    canvas.drawBitmap(bitmap, x, 0, null);
                    x = x + bitmap.getWidth();
                }
            }
        }
    }
    
    public void setVideo(@NonNull Uri data) {
        mVideoUri = data;
    }
    }
    

    And use like this

    mTimeLineView = (TimeLineView) view.findViewById(R.id.timelineview);
    mTimeLineView.setVideo(Uri.parse("video_path"));