2016-01-11 12 views
8

Chciałbym wdrożyć aplikację do przesyłania strumieniowego wideo. Użyłem RecyclerView do wyświetlenia mojego elementu listy. Typ artykułu obejmuje 4 typy: artykuł, status, zdjęcie i wideo. Powinniśmy skupiać się wyłącznie na rodzaju wideo. Tu jest mój kodu dla adaptera RecyclerView za:Streaming wideo lista za pomocą Exoplayer

public class FollowedPostAdapter extends RecyclerView.Adapter implements OnFollowTagCallback, OnLikeCallback { 
    private Context context; 
    private List<PostItem> newsFeedList; 
    public RecyclerView recyclerView; 
    public LinearLayoutManager linearLayoutManager; 
    private HashMap<Integer, DemoPlayer> playerList; 

    private int visibleThreshold = 5; 
    // private int previousTotal = 0; 
    private int visibleItemCount, firstVisibleItem, totalItemCount; 
    private boolean loading; 
    private OnRecyclerViewLoadMoreListener loadMoreListener; 


    private final String text_comment; 
    private final String mReadMoreHtml; 
    private long userId; 


    public FollowedPostAdapter(Context context, RecyclerView recyclerView, List<PostItem> newsFeedList) { 
     this.context = context; 
     playerList = new HashMap<>(); 
     this.newsFeedList = newsFeedList; 
     this.recyclerView = recyclerView; 
     this.linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); 

    } 

    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     RecyclerView.ViewHolder viewHolder; 
     if (viewType == Constants.VIEWTYPE_ARTICLE) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_article_item, parent, false); 
      viewHolder = new ArticleViewHolder(view); 
     } else if (viewType == Constants.VIEWTYPE_STATUS) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_status_item, parent, false); 
      viewHolder = new StatusViewHolder(view); 
     } else if (viewType == Constants.VIEWTYPE_PHOTO) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_photo_item, parent, false); 
      viewHolder = new PhotoViewHolder(view); 
     } else if (viewType == Constants.VIEWTYPE_VIDEO) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_video_item, parent, false); 
      viewHolder = new VideoViewHolder(view); 
     } else { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.progressbar_item, parent, false); 
      viewHolder = new ProgressBarViewHolder(view); 
     } 
     return viewHolder; 
    } 

    @Override 
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
     if (holder instanceof ArticleViewHolder) { 
      // code 

     } else if (holder instanceof StatusViewHolder) { 
     // code 

     } else if (holder instanceof PhotoViewHolder) { 
     // code 
     } else if (holder instanceof VideoViewHolder) { 
      PostItem item = newsFeedList.get(position); 
      VideoViewHolder mHolder = (VideoViewHolder) holder; 
      LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mHolder.videoView.getLayoutParams(); 
      lp.height = (int) (ScreenHelper.getScreenWidth((Activity) context) * Constants.POST_IMAGE_RATIO); 
      mHolder.videoView.setLayoutParams(lp); 
      mHolder.setup(item); 
      Picasso.with(context).load(item.imageCover).error(R.drawable.ic_user_avatar).placeholder(R.drawable.ic_user_avatar).into(mHolder.iv_avatar); 
      if (item.tags != null && item.tags.size() > 0) { 
       // get first tag as main tag 
       generateTagViews(mHolder.tag_flow_layout, item.tags.subList(0, 1), position); 
       mHolder.tag_flow_layout.setVisibility(View.VISIBLE); 
       mHolder.tag_flow_layout.setVisibility(View.VISIBLE); 
       // mHolder.indicator.setVisibility(View.VISIBLE); 
      } else { 
       mHolder.tag_flow_layout.setVisibility(View.GONE); 
       // mHolder.indicator.setVisibility(View.GONE); 
      } 
      if (item.time_created != null) { 
       mHolder.tv_time.setText(item.time_created); 
       //mHolder.indicator.setVisibility(View.VISIBLE); 
       mHolder.tv_time.setVisibility(View.VISIBLE); 
      } else { 
       //mHolder.indicator.setVisibility(View.GONE); 
       mHolder.tv_time.setVisibility(View.GONE); 
      } 
      if (item.description_short.isEmpty()) 
       mHolder.tv_description.setVisibility(View.GONE); 
      else { 
       mHolder.tv_description.setText(item.description_short); 
       mHolder.tv_description.setVisibility(View.VISIBLE); 
      } 

      mHolder.btn_comment.setText(String.valueOf(item.count_comment)); 
      mHolder.btn_like.setText(String.valueOf(item.count_like)); 
      mHolder.btn_unlike.setText(String.valueOf(item.count_unlike)); 
      mHolder.btn_share.setText(String.valueOf(item.count_share)); 
      if (item.tags.size() != 0) { 
       int tagId = item.tags.get(0).tag_id; 
       setFollowButtonActive(mHolder.btn_follow, TagHelper.isTagFollowed(tagId)); 
      } else 
       setFollowButtonActive(mHolder.btn_follow, false); 

     } 

    } 

    @Override 
    public void onViewRecycled(RecyclerView.ViewHolder holder) { 
     super.onViewRecycled(holder); 
     if (holder instanceof VideoViewHolder) { 
      DemoPlayer player = playerList.get(holder.getAdapterPosition()); 
      if (player != null) { 
       player.release(); 
       playerList.remove(holder.getAdapterPosition()); 
      } 
     } 
    } 


    public void pauseAllPlayers() { 
     for (int i = 0; i <= newsFeedList.size(); i++) { 
      DemoPlayer player = playerList.get(i); 
      if (player != null) { 
       if (player.getPlayerControl().isPlaying()) 
        player.getPlayerControl().pause(); 
       RecyclerView.ViewHolder holder = recyclerView.findViewHolderForLayoutPosition(i); 
       if (holder != null && holder instanceof VideoViewHolder) { 
        ((VideoViewHolder) holder).btn_play.setVisibility(View.VISIBLE); 
       } 

      } 
     } 
    } 





    public void refreshData() { 
     notifyDataSetChanged(); 
    } 

    public class ArticleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
     // 
    } 

    public class StatusViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 

     // code 
    } 

    public class PhotoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
     // code 
    } 

    public class VideoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, SurfaceHolder.Callback, AudioCapabilitiesReceiver.Listener, DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener { 
     @Bind(R.id.iv_avatar) 
     ImageView iv_avatar; 
     @Bind(R.id.tag_flow_layout) 
     FlowLayout tag_flow_layout; 
     @Bind(R.id.tv_time) 
     TextView tv_time; 
     @Bind(R.id.btn_follow) 
     FancyButton btn_follow; 
     @Bind(R.id.btn_comment) 
     FancyButton btn_comment; 
     @Bind(R.id.btn_like) 
     FancyButton btn_like; 
     @Bind(R.id.btn_unlike) 
     FancyButton btn_unlike; 
     @Bind(R.id.btn_share) 
     FancyButton btn_share; 
     @Bind(R.id.root) 
     FrameLayout videoView; 
     @Bind(R.id.btn_play) 
     ImageView btn_play; 
     @Bind(R.id.tv_description) 
     TextView tv_description; 

     // player's variable 
     private EventLogger eventLogger; 

     // private VideoControllerView mediaController; 
     private View shutterView; 
     private AspectRatioFrameLayout videoFrame; 
     private SurfaceView surfaceView; 
     private SubtitleLayout subtitleLayout; 

     private DemoPlayer player; 
     private boolean playerNeedsPrepare; 

     private long playerPosition = 0; 

     private Uri contentUri; 
     private int contentType; 
     private String contentId; 

     public static final int TYPE_DASH = 0; 
     public static final int TYPE_SS = 1; 
     public static final int TYPE_HLS = 2; 
     public static final int TYPE_OTHER = 3; 

     private static final String EXT_DASH = ".mpd"; 
     private static final String EXT_SS = ".ism"; 
     private static final String EXT_HLS = ".m3u8"; 

     private AudioCapabilitiesReceiver audioCapabilitiesReceiver; 

     public VideoViewHolder(View view) { 
      super(view); 
      ButterKnife.bind(this, view); 
      iv_avatar.setOnClickListener(this); 
      btn_follow.setOnClickListener(this); 
      btn_comment.setOnClickListener(this); 
      btn_like.setOnClickListener(this); 
      btn_unlike.setOnClickListener(this); 
      btn_share.setOnClickListener(this); 

      // player's setup 
      View root = view.findViewById(R.id.root); 
      root.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        doPlayResume(); 
       } 
      }); 
//   root.setOnTouchListener(new View.OnTouchListener() { 
//    @Override 
//    public boolean onTouch(View view, MotionEvent motionEvent) { 
//     if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { 
//      toggleControlsVisibility(); 
//     } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { 
//      view.performClick(); 
//     } 
//     return true; 
//    } 
//   }); 
//   root.setOnKeyListener(new View.OnKeyListener() { 
//    @Override 
//    public boolean onKey(View v, int keyCode, KeyEvent event) { 
//     if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE 
//       || keyCode == KeyEvent.KEYCODE_MENU) { 
//      return false; 
//     } 
//     return mediaController.dispatchKeyEvent(event); 
//    } 
//   }); 
      shutterView = view.findViewById(R.id.shutter); 
      videoFrame = (AspectRatioFrameLayout) view.findViewById(R.id.video_frame); 
      surfaceView = (SurfaceView) view.findViewById(R.id.surface_view); 
      surfaceView.getHolder().addCallback(this); 
      subtitleLayout = (SubtitleLayout) view.findViewById(R.id.subtitles); 
//   mediaController = new VideoControllerView(context); 
//   mediaController.setAnchorView((ViewGroup) root); 
      audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(context, this); 
      audioCapabilitiesReceiver.register(); 
     } 

     @Override 
     public void onClick(View v) { 
      switch (v.getId()) { 
       case R.id.iv_avatar: 
        viewTagDetail(getAdapterPosition()); 
        break; 
       case R.id.btn_follow: 
        followTag((FancyButton) v, getAdapterPosition()); 
        break; 
       case R.id.btn_comment: 
        commentPost(getAdapterPosition()); 
        break; 
       case R.id.btn_like: 
        likePost(getAdapterPosition(), btn_like, btn_unlike); 
        break; 
       case R.id.btn_unlike: 
        unlikePost(getAdapterPosition(), btn_like, btn_unlike); 
        break; 
       case R.id.btn_share: 
        sharePost(getAdapterPosition()); 
        break; 
       case R.id.btn_play: 
        doPlayResume(); 
        break; 
      } 
     } 

     public void setup(PostItem item) { 
      releasePlayer(); 
      player = playerList.get(getAdapterPosition()); 
      contentUri = Uri.parse(item.link); 
      contentType = TYPE_OTHER; 
      contentId = String.valueOf(item.post_id); 
      configureSubtitleView(); 
      if (player == null) { 
       preparePlayer(false); 
      } else { 
       player.setBackgrounded(false); 
      } 
     } 

     //  public void saveCurrentPosition() { 
//   if (player != null) 
//    videoItemList.get(getAdapterPosition()).position = player.getCurrentPosition(); 
//  } 
     public void doPlayResume() { 
      if (player == null) { 
       return; 
      } 

      if (player.getPlayerControl().isPlaying()) { 
       player.getPlayerControl().pause(); 
      } else { 
       player.getPlayerControl().start(); 
      } 
      showControls(); 
     } 

     @Override 
     public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) { 
      if (player == null) { 
       return; 
      } 
      boolean backgrounded = player.getBackgrounded(); 
      boolean playWhenReady = player.getPlayWhenReady(); 
      releasePlayer(); 
      preparePlayer(playWhenReady); 
      player.setBackgrounded(backgrounded); 
     } 

     private DemoPlayer.RendererBuilder getRendererBuilder() { 
      String userAgent = Util.getUserAgent(context, "ExoPlayerDemo"); 
      switch (contentType) { 
       case TYPE_SS: 
        return new SmoothStreamingRendererBuilder(context, userAgent, contentUri.toString(), 
          new SmoothStreamingTestMediaDrmCallback()); 
       case TYPE_DASH: 
        return new DashRendererBuilder(context, userAgent, contentUri.toString(), 
          new WidevineTestMediaDrmCallback(contentId)); 
       case TYPE_HLS: 
        return new HlsRendererBuilder(context, userAgent, contentUri.toString()); 
       case TYPE_OTHER: 
        return new ExtractorRendererBuilder(context, userAgent, contentUri); 
       default: 
        throw new IllegalStateException("Unsupported type: " + contentType); 
      } 
     } 

     private void preparePlayer(boolean playWhenReady) { 
      if (player == null) { 
       player = new DemoPlayer(getRendererBuilder()); 
       playerList.put(getAdapterPosition(), player); 
       player.addListener(this); 
       player.setCaptionListener(this); 
       player.setMetadataListener(this); 
       player.seekTo(playerPosition); 
       playerNeedsPrepare = true; 
//    mediaController.setMediaPlayer(player.getPlayerControl()); 
//    mediaController.setEnabled(true); 
       eventLogger = new EventLogger(); 
       eventLogger.startSession(); 
       player.addListener(eventLogger); 
       player.setInfoListener(eventLogger); 
       player.setInternalErrorListener(eventLogger); 
      } 
      if (playerNeedsPrepare) { 
       player.prepare(); 
       playerNeedsPrepare = false; 
      } 
      player.setSurface(surfaceView.getHolder().getSurface()); 
      player.setPlayWhenReady(playWhenReady); 
     } 

     private void releasePlayer() { 
      if (player != null) { 
       player.release(); 
       player = null; 
       eventLogger.endSession(); 
       eventLogger = null; 
       btn_play.setVisibility(View.VISIBLE); 
      } 
     } 

     @Override 
     public void onStateChanged(boolean playWhenReady, int playbackState) { 
      if (playbackState == ExoPlayer.STATE_ENDED) { 
       showControls(); 
      } 

     } 

     @Override 
     public void onError(Exception e) { 
      if (e instanceof UnsupportedDrmException) { 
       // Special case DRM failures. 
       UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e; 
       int stringId = Util.SDK_INT < 18 ? R.string.drm_error_not_supported 
         : unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME 
         ? R.string.drm_error_unsupported_scheme : R.string.drm_error_unknown; 
       Toast.makeText(context, stringId, Toast.LENGTH_LONG).show(); 
      } 
      playerNeedsPrepare = true; 
      showControls(); 
     } 

     @Override 
     public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { 
      shutterView.setVisibility(View.GONE); 
      videoFrame.setAspectRatio(
        height == 0 ? 1 : (width * pixelWidthHeightRatio)/height); 
     } 

     private boolean haveTracks(int type) { 
      return player != null && player.getTrackCount(type) > 0; 
     } 

//  private void toggleControlsVisibility() { 
//   if (mediaController.isShowing()) { 
//    mediaController.hide(); 
//   } else { 
//    showControls(); 
//   } 
//  } 

     private void showControls() { 
//   mediaController.show(5000); 
      if (player.getPlayerControl().isPlaying()) 
       btn_play.setVisibility(View.GONE); 
      else 
       btn_play.setVisibility(View.VISIBLE); 

     } 

     @Override 
     public void onCues(List<Cue> cues) { 
      subtitleLayout.setCues(cues); 
     } 

     @Override 
     public void onId3Metadata(Map<String, Object> metadata) { 

     } 

     @Override 
     public void surfaceCreated(SurfaceHolder holder) { 
      if (player != null) { 
       player.setSurface(holder.getSurface()); 
      } 
     } 

     @Override 
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
     } 

     @Override 
     public void surfaceDestroyed(SurfaceHolder holder) { 
      if (player != null) { 
       holder.lockCanvas(); 
       player.blockingClearSurface(); 
      } 
     } 

     private void configureSubtitleView() { 
      CaptionStyleCompat style; 
      float fontScale; 
      if (Util.SDK_INT >= 19) { 
       style = getUserCaptionStyleV19(); 
       fontScale = getUserCaptionFontScaleV19(); 
      } else { 
       style = CaptionStyleCompat.DEFAULT; 
       fontScale = 1.0f; 
      } 
      subtitleLayout.setStyle(style); 
      subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale); 
     } 

     @TargetApi(19) 
     private float getUserCaptionFontScaleV19() { 
      CaptioningManager captioningManager = 
        (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); 
      return captioningManager.getFontScale(); 
     } 

     @TargetApi(19) 
     private CaptionStyleCompat getUserCaptionStyleV19() { 
      CaptioningManager captioningManager = 
        (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); 
      return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); 
     } 

     private int inferContentType(Uri uri, String fileExtension) { 
      String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension 
        : uri.getLastPathSegment(); 
      if (lastPathSegment == null) { 
       return TYPE_OTHER; 
      } else if (lastPathSegment.endsWith(EXT_DASH)) { 
       return TYPE_DASH; 
      } else if (lastPathSegment.endsWith(EXT_SS)) { 
       return TYPE_SS; 
      } else if (lastPathSegment.endsWith(EXT_HLS)) { 
       return TYPE_HLS; 
      } else { 
       return TYPE_OTHER; 
      } 
     } 
    } 

    public class ProgressBarViewHolder extends RecyclerView.ViewHolder { 
     public ProgressBarViewHolder(View view) { 
      super(view); 
     } 
    } 

    public void unregisterEventBus() { 
     EventBus.getDefault().unregister(this); 
    } 
} 
  • Gdy typ elementu jest wideo, tworzę instancję ExoPlayer do odegrania ten film i dodać tę instancję do HashMap do późniejszego wykorzystania (z wartości klucza jest elementu pozycja). Jeśli element wideo jest poddawany recyklingowi, zwalniam odtwarzacz i usuwam go z HashMap.

  • Wszystko wygląda dobrze, ale problem. Przypuszczam, że element wideo znajduje się na pozycji 0 (teraz możemy zobaczyć podgląd wideo w tym elemencie). Przewiń w dół RecyclerView na tyle, aby ukryć element 0. W tej chwili VideoViewHolder pozycji 0 nie jest jeszcze przetwarzany. Następnie przewijam w górę, aby wyświetlić element 0. Widok jest teraz pusty, a nie pokazuje podglądu wideo. Klikam element 0, aby odtworzyć wideo, odtwarzacz odtwarza tylko dźwięk (dźwięk), a nie wyświetla wideo. Po odtworzeniu kilku sekund wideo jest teraz widoczne.

  • Po debugowaniu i stwierdzeniu, że po przewinięciu w dół, aby ukryć element wideo, zostanie zniszczony kod SurfaceView. Podczas przewijania w górę w celu odsłonięcia elementu wideo zostanie utworzony SurfaceView. Myślę, że to jest powód, dla którego VideoViewHolder pokazuje pusty widok zamiast podglądu wideo.

  • Oto moje pytanie: Jak wyświetlić podgląd wideo po przewinięciu do elementu wideo?

+0

Znaleźliście jakieś rozwiązanie? Mam też ten problem – Hani

Odpowiedz

0

Oto notatka z ExoPlayer documentation

renderowania SurfaceView nie został prawidłowo zsynchronizowany z widokiem animacji aż Android N. na wcześniejszych wersjach może to prowadzić do niepożądanych skutków, kiedy SurfaceView został umieszczony w pojemniku przewijania lub kiedy został poddany animacji. Takie efekty obejmowały zawartość SurfaceView, która wydaje się pozostawać w tyle za miejscem, w którym powinna być wyświetlana, a widok zmienia kolor na czarny po poddaniu animacji.

Aby uzyskać płynną animację lub przewijanie wideo przed Androidem N, konieczne jest użycie TextureView zamiast SurfaceView. Jeśli gładki animacja lub przewijanie nie jest wymagana wtedy SurfaceView powinny być preferowane

Aby włączyć TextureView surface_type trzeba ustawić go w pliku xml, jak pokazano poniżej

<com.google.android.exoplayer2.ui.SimpleExoPlayerView 
    android:id="@+id/playerView" 
    android:layout_width="match_parent" 
    android:layout_height="200dp" 
    app:surface_type="texture_view"/> 
Powiązane problemy