通过 FFmpegMediaMetadataRetriever 检索视频的多个帧时出错

Error in retrieving multiple frames of a video via FFmpegMediaMetadataRetriever

我正在尝试从视频中获取多个帧,但它有时会出现致命异常。我在这里做的是获取框架一段时间,如果方向是纵向,则旋转。失败的原因可能是内存问题。请指出我哪里错了。

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;
}

错误日志

我知道 post 回答已经太晚了,但它仍然可以帮助某些人。下面class可以用来显示视频帧数

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;
}
}

然后像这样使用

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