使用媒体浏览器类型的应用程序,我使用自定义驱动程序从USB读取文件。文件由NanoHTTPServer
公开。这里不重要,只是为了获取信息。
以下代码逻辑工作得很好,但它看起来很糟糕,并开始在快速滚动中挣扎。 有没有办法更快地加载视频帧,避免挂起和延迟帧。这段代码有什么缺陷吗?因为我正在使用rxjava。
@Override
public void onBindViewHolder(BasicHolder holder,final int position) {
...
if(multiMedia.isImage()){
Glide.with(context)
.load(multiMedia.getUrl())
.crossFade()
.into(holder.imageThumbnail);
}else{
Bitmap bitmap = mThumbnailsCache.get(multiMedia.getUrl()+THUMBNAIL_AT_PERCENT);
if(bitmap == null || multiMedia.getLength() == 0){
//check for already running task
if(holder.imageThumbnail.getTag(R.string.tag_for_video) == null){
loadVideoThumbnail(holder,multiMedia,position);
}
}else {
holder.imageThumbnail.setImageBitmap(bitmap);
}
}
....
//this method will trigger rxjava task on io and set back results emitted by
private void loadVideoThumbnail(final BasicHolder holder, final MultiMedia multiMedia,final int position){
final SoftReference<BasicHolder> mHolderRef = new SoftReference<>(holder);
final Subscription sub = extractFrameAndDuration(multiMedia.getUrl(), THUMBNAIL_AT_PERCENT)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(emittedObject -> {
if (emittedObject == null || mHolderRef.get() == null) return;
if (mHolderRef.get().getLayoutPosition() != position) {
notifyItemChanged(position);
return;
}
if (emittedObject instanceof Bitmap) {
ImageView imageThumbnail = mHolderRef.get().imageThumbnail;
if (imageThumbnail.getTag(R.string.tag_for_video) != null) {
imageThumbnail.setTag(R.string.tag_for_video, null);
imageThumbnail.setImageBitmap((Bitmap) emittedObject);
}
} else {
multiMedia.setLength((long)emittedObject);
TextView textInfo = mHolderRef.get().textInfo;
if (textInfo.getTag() == Filetype.MOVIE) {
mHolderRef.get().viewHighlight.setData(multiMedia.getSavedClips(),multiMedia.getLength());
textInfo.setText(VideoUtils.getTotalClipDuration((long) emittedObject, App.context));
}
}
}, Throwable::printStackTrace);
if(reqSubQueue.size() > 8){
//cancel previous requests
reqSubQueue.pop().unsubscribe();
}
reqSubQueue.add(sub);
holder.imageThumbnail.setTag(R.string.tag_for_video,sub);
}
...
//this method will get thumbnail from video url
private Observable<Object> extractFrameAndDuration(String url, final long atPercent){
return Observable.create(subscriber -> {
final MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
subscriber.add(new Subscription() {
@Override
public void unsubscribe() {
//this will actually cancel loading
Observable.fromCallable(() -> {
mediaMetadataRetriever.release();
return true;
}).subscribeOn(Schedulers.io())
.subscribe();
}
@Override
public boolean isUnsubscribed() {
return false;
}
});
try{
mediaMetadataRetriever.setDataSource(url, new HashMap<>());
String durationString = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
final long duration = Long.parseLong(TextUtils.isEmpty(durationString)? "0": durationString);
//cache duration
clipsDurations.put(url,duration);
subscriber.onNext(duration);
long timeUs = (long) (duration / 100.0 * atPercent);
if(timeUs > duration) timeUs = duration;
Bitmap bitmap = mThumbnailsCache.get(url+THUMBNAIL_AT_PERCENT);
if(bitmap == null){
//1 milli sec = 1000 microseconds
bitmap = MediaUtils.getFrameFromVideoAt(mediaMetadataRetriever, timeUs * 1000);
if(bitmap != null){
bitmap = MediaUtils.resize(bitmap,thumbSize,thumbSize);
mThumbnailsCache.put(url+THUMBNAIL_AT_PERCENT,bitmap);
subscriber.onNext(bitmap);
}
}else {
subscriber.onNext(bitmap);
}
}
catch (Exception e){
e.printStackTrace();
}
finally{
if (mediaMetadataRetriever != null){
mediaMetadataRetriever.release();
}
}
//remove item from queue
subscriber.onCompleted();
reqSubQueue.remove(subscriber);
});
}
所有请求都会在Activity.onStop
中被取消。 reqSubQueue
为ArrayDeque
编辑方法说明
我相信一些亲开发者会发现缺陷。