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