在recyclerview中第一帧高效加载图像和视频

时间:2017-05-25 08:09:51

标签: android android-recyclerview video-streaming rx-java

使用媒体浏览器类型的应用程序,我使用自定义驱动程序从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中被取消。 reqSubQueueArrayDeque

添加

编辑方法说明

我相信一些亲开发者会发现缺陷。

0 个答案:

没有答案