【问题标题】:Can VideoView be detach and reattached without stopping the stream?可以在不停止流的情况下分离和重新连接 VideoView 吗?
【发布时间】:2013-11-10 20:46:24
【问题描述】:

我正在构建一个应用程序,用户可以在其中单击按钮以全屏显示视频。最初,视频附加到 ViewPager 内的视图。为了能够全屏显示它,我将它与父级分离并将其重新附加到根视图。这工作正常,除非在播放时将视频切换到全屏。当我分离正在播放的 VideoView 时,它会停止,我需要重新启动它。这是不可接受的,因为视频在恢复之前开始缓冲。这是完成分离的代码部分:

    final ViewGroup parent = (ViewGroup) findViewById(R.id.parent);

    final ViewGroup root = (ViewGroup) findViewById(R.id.root);

    Button b = (Button) findViewById(R.id.button);
    b.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            parent.removeView(mVideoView);

            LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            root.addView(mVideoView, lp);
        }
    });

根据设备的不同,我有不同的日志错误。可能是因为实际的视频播放器是由制造商提供的,而不是 Android SDK。以下是 Nexus 7 的错误日志:

10-30 20:26:18.618: D/NvOsDebugPrintf(124): NvMMDecTVMRDestroyParser Begin 
10-30 20:26:18.618: D/NvOsDebugPrintf(124): --------- Closing TVMR Frame Delivery Thread -------------
10-30 20:26:18.678: D/NvOsDebugPrintf(124): ------- NvAvpClose -------
10-30 20:26:18.678: D/NvOsDebugPrintf(124): NvMMDecTVMRDestroyParser Done 
10-30 20:26:18.678: D/NvOsDebugPrintf(124): NvMMLiteTVMRDecPrivateClose Done 

我无法在不停止视频的情况下将其分离。我尝试使用 SurfaceView 或 TextureView 没有成功。

我还尝试寻找第三方视频播放器。我发现了一个商业用途 (http://www.vitamio.org/),我不能真正用于商业原因。我找到了一个开源的,去年没有更新过 (https://code.google.com/p/dolphin-player/)。

我目前仅在平板电脑上针对 Android 4.2 或更高版本。


请注意,ViewPager 不是全屏的。所以我不能使用 LayoutParams 来制作视频全屏。我需要从 ViewPager 中的父级中删除 VideoView 并将其添加到根视图才能全屏显示。

我正在测试的 URL:http://bellvps1.cpl.delvenetworks.com/media/e1b3e24ecb944abd8f4ed823a0b76ddc/68f78d35296243bfb46d2418f03f2fd0/bande-annonce---the-secret-life-of-walter-mitty-1-9efcc5c6e52ac07a3edf84a1b21967995b7796a2.m3u8

【问题讨论】:

  • 如果您有时间尝试扩展 VideoView 并覆盖 onDetachedFromWindow():使用空的不要调用 super。
  • Answer here 可能会给你一些关于如何使用 SurfaceView 的线索。
  • 为什么不将seekTo() 方法与getCurrentPosition() 结合使用?我可以看到VideoView 实现了MediaController
  • 而不是从“父”中删除,只需设置“父”全屏(MATCH_PARENT)的 LayoutParams,所以我认为您不需要从父视图中删除视频视图并添加到根视图。在这里,您尝试仅调整视频视图的大小,也尝试将您的“父”视图调整为全屏并评论“parent.removeView(mVideoView);”代码。
  • @HirenDabhi 将父级设置为 MATCH_PARENT 无法正常工作,因为我想在 ViewPager 中保持相同的布局

标签: android ffmpeg android-videoview vitamio


【解决方案1】:

这个效果可以使用TextureView来实现。 本质上,您所做的是创建一个 MediaPlayer 实例,prepare() 它并 ​​start() 它,然后您可以在任何 TextureView 上使用 MediaPlayer.setSurface() 方法在视频播放时更改表面,而无需对 @987654327 进行任何更改@ 对象状态,如 android 文档中针对 setSurface() 方法所述:

This method can be called in any state and calling it does not change the object state.

请注意这个实现是为了演示,你可能应该使用mediaplayer.prepareAsync()并等待onPreparedListener()的回调,你还需要根据你的视频为第二个TextureView设置正确的尺寸大小、处理方向变化,当然还可以在需要时正确处理异常。

activity_main.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:background="#ff0000" />

    <Button
        android:id="@+id/btn"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:text="Switch to second surface" />

    <TextureView
        android:id="@+id/tv_full"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

MainActivity.java

public class MainActivity extends Activity {
    private static final String VIDEO_URI = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";

    private TextureView tvFull;
    private MediaPlayer mp;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mp = new MediaPlayer();
        try {
            mp.setDataSource(this, Uri.parse(VIDEO_URI));
            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mp.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }

        ((ViewPager) findViewById(R.id.pager)).setAdapter(new PagerAdapter() {
            @Override public int getCount() {
                return 1;
            }

            @Override public boolean isViewFromObject(View view, Object o) {
                return view == o;
            }

            @Override public Object instantiateItem(ViewGroup container, int position) {
                final TextureView tv = new TextureView(MainActivity.this);

                tv.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
                    @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
                        tv.setSurfaceTextureListener(null);
                        mp.setSurface(new Surface(surfaceTexture));
                        mp.start();
                    }

                    @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {}

                    @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
                        return false;
                    }

                    @Override public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}
                });

                if (tv.isAvailable()) {
                    tv.getSurfaceTextureListener().onSurfaceTextureAvailable(tv.getSurfaceTexture(),
                            tv.getWidth(), tv.getHeight());
                }

                container.addView(tv, 0);
                return tv;
            }

            @Override public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView((View) object);
            }
        });

        tvFull = (TextureView) findViewById(R.id.tv_full);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View view) {
                tvFull.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
                    @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
                        tvFull.setSurfaceTextureListener(null);
                        mp.setSurface(new Surface(surfaceTexture));
                    }

                    @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {

                    }

                    @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
                        return false;
                    }

                    @Override public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

                    }
                });

                if (tvFull.isAvailable()) {
                    tvFull.getSurfaceTextureListener().onSurfaceTextureAvailable(tvFull.getSurfaceTexture(),
                            tvFull.getWidth(), tvFull.getHeight());
                }
            }
        });
    }

    @Override protected void onDestroy() {
        mp.reset();
        mp.release();

        super.onDestroy();
    }
}

【讨论】:

  • 这个解决方案在技术上是可行的,但这不是我想要的用户体验。 :) 首先,视频停了几毫秒。不幸的是,它很明显。其次,使用我的测试网址(见上文),当表面发生变化时,视频视图不会 100% 刷新。所以直到一帧完全刷新屏幕,我看到黑色像素。无论如何感谢您的努力。你应得的赏金。但我不认为我想做的事情是可行的。 :)
  • @Thierry-DimitriRoy 这可能是由于您为此视频使用的特定编码设置,也许这​​可以进一步帮助:stackoverflow.com/questions/19299412/…
  • 我不这么认为。 MPEG 被构建为只绘制帧之间变化的像素。这就是为什么我在屏幕上有这些黑色部分。并且没有办法强制刷新屏幕。 :(
【解决方案2】:

没试过,希望对你有帮助

        final ViewGroup parent = (ViewGroup) findViewById(R.id.parent);

    final ViewGroup root = (ViewGroup) findViewById(R.id.root);//assuming this full screen

    Button b = (Button) findViewById(R.id.button);
    b.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // parent.removeView(mVideoView);
            LayoutParams parentParam = parent.getLayoutParams();
            parentParam.height = root.getLayoutParams().height;
            parentParam.width = root.getLayoutParams().width;
            parent.setLayoutParams(parentParam);
            LayoutParams lp = new FrameLayout.LayoutParams(
                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            root.addView(mVideoView, lp);
        }
    });

谢谢。

【讨论】:

    【解决方案3】:

    我不知道视频播放器的工作原理,但我的直觉是你必须:

    Override onSaveInstanceState to save the place in the video (maybe a timestamp?)
    
    Override onRestoreInstanceState to reload the video and seek to the point saved in step 1
    

    【讨论】:

      【解决方案4】:

      在此修改中使用纹理视图:

      @Override 
                  public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
      
                      if (mediaPlayer != null) {
                          mediaPlayer.setSurface(new Surface (surfaceTexture));}
                  }
      

      更新:以及完整代码: player2.java:

      public class player2 extends TextureView
          implements MediaPlayerControl {
          private String TAG = "TextureVideoView";
          // settable by the client
          private Uri         mUri;
          private Map<String, String> mHeaders;
      
          // all possible internal states
          private static final int STATE_ERROR              = -1;
          private static final int STATE_IDLE               = 0;
          private static final int STATE_PREPARING          = 1;
          private static final int STATE_PREPARED           = 2;
          private static final int STATE_PLAYING            = 3;
          private static final int STATE_PAUSED             = 4;
          private static final int STATE_PLAYBACK_COMPLETED = 5;
      
          private static int mCurrentState;
          private static int mTargetState;
      
      
          // All the stuff we need for playing and showing a video
          static public SurfaceTexture sf;
          static public MediaPlayer mMediaPlayer;
          private int         mAudioSession;
          private int         mVideoWidth;
          private int         mVideoHeight;
          private int         mSurfaceWidth;
          private int         mSurfaceHeight;
          private  MediaController mMediaController;
          private OnCompletionListener mOnCompletionListener;
          private MediaPlayer.OnPreparedListener mOnPreparedListener;
          private int         mCurrentBufferPercentage;
          private OnErrorListener mOnErrorListener;
          private OnInfoListener  mOnInfoListener;
          private int         mSeekWhenPrepared;  // recording the seek position while preparing
          private static boolean     mCanPause;
          private static boolean     mCanSeekBack;
          private static boolean     mCanSeekForward;
          private Context mContext;
      
          public player2(Context context) {
              super(context);
             initVideoView();
          }
      
          public player2(Context context, AttributeSet attrs) {
              this(context, attrs, 0);
              initVideoView();
          }
      
          public player2(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
              initVideoView();
          }
      
          @Override
          protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
              //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
              //        + MeasureSpec.toString(heightMeasureSpec) + ")");
      
              int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
              int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
              if (mVideoWidth > 0 && mVideoHeight > 0) {
      
                  int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
                  int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
                  int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
                  int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
      
                  if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
                      // the size is fixed
                      width = widthSpecSize;
                      height = heightSpecSize;
      
                      // for compatibility, we adjust size based on aspect ratio
                      if ( mVideoWidth * height  < width * mVideoHeight ) {
                          //Log.i("@@@", "image too wide, correcting");
                          width = height * mVideoWidth / mVideoHeight;
                      } else if ( mVideoWidth * height  > width * mVideoHeight ) {
                          //Log.i("@@@", "image too tall, correcting");
                          height = width * mVideoHeight / mVideoWidth;
                      }
                  } else if (widthSpecMode == MeasureSpec.EXACTLY) {
                      // only the width is fixed, adjust the height to match aspect ratio if possible
                      width = widthSpecSize;
                      height = width * mVideoHeight / mVideoWidth;
                      if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                          // couldn't match aspect ratio within the constraints
                          height = heightSpecSize;
                      }
                  } else if (heightSpecMode == MeasureSpec.EXACTLY) {
                      // only the height is fixed, adjust the width to match aspect ratio if possible
                      height = heightSpecSize;
                      width = height * mVideoWidth / mVideoHeight;
                      if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                          // couldn't match aspect ratio within the constraints
                          width = widthSpecSize;
                      }
                  } else {
                      // neither the width nor the height are fixed, try to use actual video size
                      width = mVideoWidth;
                      height = mVideoHeight;
                      if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                          // too tall, decrease both width and height
                          height = heightSpecSize;
                          width = height * mVideoWidth / mVideoHeight;
                      }
                      if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                          // too wide, decrease both width and height
                          width = widthSpecSize;
                          height = width * mVideoHeight / mVideoWidth;
                      }
                  }
              } else {
                  // no size yet, just adopt the given spec sizes
              }
              setMeasuredDimension(width, height);
          }
      
          @Override
          public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
              super.onInitializeAccessibilityEvent(event);
              event.setClassName(player2.class.getName());
          }
      
          @Override
          public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
              super.onInitializeAccessibilityNodeInfo(info);
              info.setClassName(player2.class.getName());
          }
      
          public int resolveAdjustedSize(int desiredSize, int measureSpec) {
              return getDefaultSize(desiredSize, measureSpec);
          }
      
          private void initVideoView() {
              mContext = getContext();
              mVideoWidth = 0;
              mVideoHeight = 0;
              setSurfaceTextureListener(mSurfaceTextureListener);
              setFocusable(true);
              setFocusableInTouchMode(true);
              requestFocus();
              mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>();
              if (mMediaPlayer==null)  {
              mCurrentState = STATE_IDLE;
              mTargetState  = STATE_IDLE;}
          }
      
          public void setVideoPath(String path) {
              setVideoURI(Uri.parse(path));
          }
      
          public void setVideoURI(Uri uri) {
              setVideoURI(uri, null);
          }
      
          /**
           * @hide
           */
          private void setVideoURI(Uri uri, Map<String, String> headers) {
              mUri = uri;
              mHeaders = headers;
              mSeekWhenPrepared = 0;
              requestLayout();
              invalidate();
          }
      
          private Vector<Pair<InputStream, MediaFormat>> mPendingSubtitleTracks;
      
          public void stopPlayback() {
              if (mMediaPlayer != null) {
                  mMediaPlayer.stop();
                  mMediaPlayer.release();
                  mMediaPlayer = null;
                  mCurrentState = STATE_IDLE;
                  mTargetState  = STATE_IDLE;
              }
          }
          private void setListeners() {
              mMediaPlayer.setOnPreparedListener(mPreparedListener);
              mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
              mMediaPlayer.setOnCompletionListener(mCompletionListener);
              mMediaPlayer.setOnErrorListener(mErrorListener);
              mMediaPlayer.setOnInfoListener(mInfoListener);
              mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
              mCurrentBufferPercentage = 0;
              mMediaPlayer.setScreenOnWhilePlaying(true);
          }
          private void openVideo() {
              if (mUri == null || sf == null) {
                  // not ready for playback just yet, will try again later
                  return;
              }
              // Tell the music playback service to pause
              // TODO: these constants need to be published somewhere in the framework.
             /* Intent i = new Intent("com.android.music.musicservicecommand");
              i.putExtra("command", "pause");
              mContext.sendBroadcast(i);*/
              if (mMediaPlayer==null) {
              try {
                  mMediaPlayer = new MediaPlayer();
      
                  if (mAudioSession != 0) {
                      mMediaPlayer.setAudioSessionId(mAudioSession);
                  } else {
                      mAudioSession = mMediaPlayer.getAudioSessionId();
                  }
                  setListeners();
      
                  mMediaPlayer.setSurface(new Surface (sf));
                  mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
                  mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                  mMediaPlayer.prepareAsync();
                  // we don't set the target state here either, but preserve the
                  // target state that was there before.
                  mCurrentState = STATE_PREPARING;
                  attachMediaController();
              } catch (IOException ex) {
                  Log.w(TAG, "Unable to open content: " + mUri, ex);
                  mCurrentState = STATE_ERROR;
                  mTargetState = STATE_ERROR;
                  mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
                  return;
              } catch (IllegalArgumentException ex) {
                  Log.w(TAG, "Unable to open content: " + mUri, ex);
                  mCurrentState = STATE_ERROR;
                  mTargetState = STATE_ERROR;
                  mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
                  return;
              } finally {
                  mPendingSubtitleTracks.clear();
              }} else {
                  setListeners();
              }
          }
      
          public void setMediaController(MediaController controller) {
              if (mMediaController != null) {
                  mMediaController.hide();
              }
              mMediaController = controller;
              attachMediaController();
          }
      
          private void attachMediaController() {
              if (mMediaPlayer != null && mMediaController != null) {
                  mMediaController.setMediaPlayer(this);
                  View anchorView = this.getParent() instanceof View ?
                      (View)this.getParent() : this;
                  mMediaController.setAnchorView(anchorView);
                  mMediaController.setEnabled(isInPlaybackState());
              }
          }
      
          MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
              new MediaPlayer.OnVideoSizeChangedListener() {
                  public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
                      mVideoWidth = mp.getVideoWidth();
                      mVideoHeight = mp.getVideoHeight();
                      if (mVideoWidth != 0 && mVideoHeight != 0) {
                          getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight);
                          requestLayout();
                      }
                  }
              };
      
          MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
              public void onPrepared(MediaPlayer mp) {
                  mCurrentState = STATE_PREPARED;
      
                  mCanPause = mCanSeekBack = mCanSeekForward = true;
      
                  if (mOnPreparedListener != null) {
                      mOnPreparedListener.onPrepared(mMediaPlayer);
                  }
                  if (mMediaController != null) {
                      mMediaController.setEnabled(true);
                  }
                  mVideoWidth = mp.getVideoWidth();
                  mVideoHeight = mp.getVideoHeight();
      
                  int seekToPosition = mSeekWhenPrepared;  // mSeekWhenPrepared may be changed after seekTo() call
                  if (seekToPosition != 0) {
                      seekTo(seekToPosition);
                  }
                  if (mVideoWidth != 0 && mVideoHeight != 0) {
                      //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
                      getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight);
                      requestLayout();
                      if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
                          // We didn't actually change the size (it was already at the size
                          // we need), so we won't get a "surface changed" callback, so
                          // start the video here instead of in the callback.
                          if (mTargetState == STATE_PLAYING) {
                              start();
                              if (mMediaController != null) {
                                  mMediaController.show();
                              }
                          } else if (!isPlaying() &&
                              (seekToPosition != 0 || getCurrentPosition() > 0)) {
                              if (mMediaController != null) {
                                  // Show the media controls when we're paused into a video and make 'em stick.
                                  mMediaController.show(0);
                              }
                          }
                      }
                  } else {
                      // We don't know the video size yet, but should start anyway.
                      // The video size might be reported to us later.
                      if (mTargetState == STATE_PLAYING) {
                          start();
                      }
                  }
              }
          };
      
          private MediaPlayer.OnCompletionListener mCompletionListener =
              new MediaPlayer.OnCompletionListener() {
                  public void onCompletion(MediaPlayer mp) {
                  if (mCurrentState == STATE_PLAYBACK_COMPLETED) {
                      return;
                  }
                      mCurrentState = STATE_PLAYBACK_COMPLETED;
                      mTargetState = STATE_PLAYBACK_COMPLETED;
                      if (mMediaController != null) {
                          mMediaController.hide();
                      }
                      if (mOnCompletionListener != null) {
                          mOnCompletionListener.onCompletion(mMediaPlayer);
                      }
                  }
              };
      
          private MediaPlayer.OnInfoListener mInfoListener =
              new MediaPlayer.OnInfoListener() {
                  public  boolean onInfo(MediaPlayer mp, int arg1, int arg2) {
                      if (mOnInfoListener != null) {
                          mOnInfoListener.onInfo(mp, arg1, arg2);
                      }
                      return true;
                  }
              };
      
          private MediaPlayer.OnErrorListener mErrorListener =
              new MediaPlayer.OnErrorListener() {
                  public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
                      Log.d(TAG, "Error: " + framework_err + "," + impl_err);
                      mCurrentState = STATE_ERROR;
                      mTargetState = STATE_ERROR;
                      if (mMediaController != null) {
                          mMediaController.hide();
                      }
      
                  /* If an error handler has been supplied, use it and finish. */
                      if (mOnErrorListener != null) {
                          if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {
                              return true;
                          }
                      }
                      if (getWindowToken() != null) {
                         // Resources r = mContext.getResources();
                          int messageId;
      
                          if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
                              messageId = R.string.VideoView_error_text_invalid_progressive_playback;
                          } else {
                              messageId = R.string.VideoView_error_text_unknown;
                          }
      
                          new AlertDialog.Builder(mContext)
                              .setMessage(messageId)
                              .setPositiveButton(R.string.VideoView_error_button,
                                  new DialogInterface.OnClickListener() {
                                      public void onClick(DialogInterface dialog, int whichButton) {
                                              /* If we get here, there is no onError listener, so
                                               * at least inform them that the video is over.
                                               */
                                          if (mOnCompletionListener != null) {
                                              mOnCompletionListener.onCompletion(mMediaPlayer);
                                          }
                                      }
                                  })
                              .setCancelable(false)
                              .show();
                      }
                      return true;
                  }
              };
      
          private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
              new MediaPlayer.OnBufferingUpdateListener() {
                  public void onBufferingUpdate(MediaPlayer mp, int percent) {
                      mCurrentBufferPercentage = percent;
                  }
              };
      
          /**
           * Register a callback to be invoked when the media file
           * is loaded and ready to go.
           *
           * @param l The callback that will be run
           */
          public void setOnPreparedListener(MediaPlayer.OnPreparedListener l)
          {
              mOnPreparedListener = l;
          }
      
          /**
           * Register a callback to be invoked when the end of a media file
           * has been reached during playback.
           *
           * @param l The callback that will be run
           */
          public void setOnCompletionListener(OnCompletionListener l)
          {
              mOnCompletionListener = l;
          }
      
          /**
           * Register a callback to be invoked when an error occurs
           * during playback or setup.  If no listener is specified,
           * or if the listener returned false, TextureVideoView will inform
           * the user of any errors.
           *
           * @param l The callback that will be run
           */
          public void setOnErrorListener(OnErrorListener l)
          {
              mOnErrorListener = l;
          }
      
          /**
           * Register a callback to be invoked when an informational event
           * occurs during playback or setup.
           *
           * @param l The callback that will be run
           */
          public void setOnInfoListener(OnInfoListener l) {
              mOnInfoListener = l;
          }
      
          TextureView.SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener()
          {
              @Override
              public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) {
                  if (mMediaPlayer!=null) {
                      mVideoWidth = mMediaPlayer.getVideoWidth();
                      mVideoHeight = mMediaPlayer.getVideoHeight();
                      if (mVideoWidth != 0 && mVideoHeight != 0) {
                          surface.setDefaultBufferSize(mVideoWidth, mVideoHeight);
                          requestLayout();
                      }}
              }
      
              @Override
              public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) {
                 sf=surface;
                 mSurfaceWidth = width;
                 mSurfaceHeight = height;
                  if (mMediaPlayer!=null) { mMediaPlayer.setSurface(new Surface (surface));
                } 
                  openVideo();
              }
      
      
              @Override
              public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) {
                  return false;
              }
              @Override
              public void onSurfaceTextureUpdated(final SurfaceTexture surface) {}
          };
      
          /*
           * release the media player in any state
           */
          private void release(boolean cleartargetstate) {
              if (mMediaPlayer != null) {
                  mMediaPlayer.reset();
                  mMediaPlayer.release();
                  mMediaPlayer = null;
                  mPendingSubtitleTracks.clear();
                  mCurrentState = STATE_IDLE;
                  if (cleartargetstate) {
                      mTargetState  = STATE_IDLE;
                  }
              }
          }
      
          @Override
          public boolean onTouchEvent(MotionEvent ev) {
              if (isInPlaybackState() && mMediaController != null) {
                  toggleMediaControlsVisiblity();
              }
              return false;
          }
      
          @Override
          public boolean onTrackballEvent(MotionEvent ev) {
              if (isInPlaybackState() && mMediaController != null) {
                  toggleMediaControlsVisiblity();
              }
              return false;
          }
      
          @Override
          public boolean onKeyDown(int keyCode, KeyEvent event)
          {
              boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
                  keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
                  keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
                  keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
                  keyCode != KeyEvent.KEYCODE_MENU &&
                  keyCode != KeyEvent.KEYCODE_CALL &&
                  keyCode != KeyEvent.KEYCODE_ENDCALL;
              if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
                  if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
                      keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
                      if (mMediaPlayer.isPlaying()) {
                          pause();
                          mMediaController.show();
                      } else {
                          start();
                          mMediaController.hide();
                      }
                      return true;
                  } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
                      if (!mMediaPlayer.isPlaying()) {
                          start();
                          mMediaController.hide();
                      }
                      return true;
                  } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
                      || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
                      if (mMediaPlayer.isPlaying()) {
                          pause();
                          mMediaController.show();
                      }
                      return true;
                  } else {
                      toggleMediaControlsVisiblity();
                  }
              }
      
              return super.onKeyDown(keyCode, event);
          }
      
          private void toggleMediaControlsVisiblity() {
              if (mMediaController.isShowing()) {
                  mMediaController.hide();
              } else {
                  mMediaController.show();
              }
          }
      
          @Override
          public void start() {
              if (isInPlaybackState()) {
                  mMediaPlayer.start();
                  mCurrentState = STATE_PLAYING;
              }
              mTargetState = STATE_PLAYING;
          }
      
          @Override
          public void pause() {
              if (isInPlaybackState()) {
                  if (mMediaPlayer.isPlaying()) {
                      mMediaPlayer.pause();
                      mCurrentState = STATE_PAUSED;
                  }
              }
              mTargetState = STATE_PAUSED;
          }
      
          public void suspend() {
              release(false);
          }
      
          public void resume() {
              openVideo();
          }
      
          @Override
          public int getDuration() {
              if (isInPlaybackState()) {
                  return mMediaPlayer.getDuration();
              }
      
              return -1;
          }
      
          @Override
          public int getCurrentPosition() {
              if (isInPlaybackState()) {
                  return mMediaPlayer.getCurrentPosition();
              }
              return 0;
          }
      
          @Override
          public void seekTo(int msec) {
              if (isInPlaybackState()) {
                  mMediaPlayer.seekTo(msec);
                  mSeekWhenPrepared = 0;
              } else {
                  mSeekWhenPrepared = msec;
              }
          }
      
          @Override
          public boolean isPlaying() {
              return isInPlaybackState() && mMediaPlayer.isPlaying();
          }
      
          @Override
          public int getBufferPercentage() {
              if (mMediaPlayer != null) {
                  return mCurrentBufferPercentage;
              }
              return 0;
          }
      
          private boolean isInPlaybackState() {
              return (mMediaPlayer != null &&
                  mCurrentState != STATE_ERROR &&
                  mCurrentState != STATE_IDLE &&
                  mCurrentState != STATE_PREPARING);
          }
      
          @Override
          public boolean canPause() {
              return mCanPause;
          }
      
          @Override
          public boolean canSeekBackward() {
              return mCanSeekBack;
          }
      
          @Override
          public boolean canSeekForward() {
              return mCanSeekForward;
          }
      
          public int getAudioSessionId() {
              if (mAudioSession == 0) {
                  MediaPlayer foo = new MediaPlayer();
                  mAudioSession = foo.getAudioSessionId();
                  foo.release();
              }
              return mAudioSession;
          }
      
      }
      

      还有一些MainActivity的代码:

         public class MainActivity extends Activity {
              static View frame1;
              static player2 textureView;
              static boolean pause=false;
              @Override
              protected void onCreate(Bundle savedInstanceState) {
      ...
              }
              @Override
              protected void onDestroy() {
                  pause=false;
                  super.onDestroy();
              }
              @Override
              public void onPause() {
                  pause=true;
                  super.onPause();
              }
              @Override
              public void onResume() {
                  if ((player2.sf!=null)&&(pause==true)&&(textureView.getSurfaceTexture()==null)) {
                      textureView.setSurfaceTexture(player2.sf);}
                  super.onResume();
              }
              public static class PlaceholderFragment extends Fragment  {
      
      
                  public PlaceholderFragment() {}
      
                  @Override
                  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                          Bundle savedInstanceState) {
                      View rootView = inflater.inflate(R.layout.fragment_main, container,
                              false);
                      frame1=rootView.findViewById(R.id.frame1);
      
      
                      textureView = (player2)rootView.findViewById(R.id.textureView1);
                      textureView.setVideoPath("http://master255.org/res/Клипы/S/SKRILLEX/Skrillex - Summit (feat. Ellie Goulding) [Video by Pilerats].mp4");
                      textureView.setMediaController(new mediac(getActivity(), frame1));
                      if (player2.sf != null) {
                          if ((pause==false)&&(textureView.getSurfaceTexture()==null)) {
                              textureView.setSurfaceTexture(player2.sf);}
                      }
      
                      textureView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                          @Override
                          public void onPrepared(final MediaPlayer mp) {
                              textureView.start();
                          }
                      });
                      return rootView;
                  }
                  public class mediac extends MediaController
                  {
      
                      public mediac(Context context, View anchor)
                      {
                          super(context);
                          super.setAnchorView(anchor);
                      }
      
                      @Override
                      public void setAnchorView(View view)
                      {}
      
                  }
              }
      
          }
      

      【讨论】:

      • 使用我的测试网址(见上文),当表面发生变化时,视频视图不会 100% 刷新。无论如何,感谢您提出的解决方案! :)
      • 我有工作示例!测试我的项目github.com/master255/TextureVideoPlayer
      【解决方案5】:

      VideoView 扩展了 SurfaceView 并且依赖于 surfaceView 的生命周期。当surfaceView 被销毁时,媒体播放器被释放并且不保留状态。结果,当您分离 VideoView 时,表面被破坏并释放媒体播放器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-30
        • 1970-01-01
        • 1970-01-01
        • 2011-02-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-29
        相关资源
        最近更新 更多