• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.content.browser;
6 
7 import android.app.Activity;
8 import android.app.AlertDialog;
9 import android.content.Context;
10 import android.content.DialogInterface;
11 import android.graphics.Color;
12 import android.util.Log;
13 import android.view.Gravity;
14 import android.view.KeyEvent;
15 import android.view.MotionEvent;
16 import android.view.Surface;
17 import android.view.SurfaceHolder;
18 import android.view.SurfaceView;
19 import android.view.View;
20 import android.view.ViewGroup;
21 import android.widget.FrameLayout;
22 import android.widget.LinearLayout;
23 import android.widget.MediaController;
24 import android.widget.ProgressBar;
25 import android.widget.TextView;
26 
27 import org.chromium.base.CalledByNative;
28 import org.chromium.base.JNINamespace;
29 import org.chromium.base.ThreadUtils;
30 import org.chromium.ui.base.ViewAndroid;
31 import org.chromium.ui.base.ViewAndroidDelegate;
32 import org.chromium.ui.base.WindowAndroid;
33 
34 @JNINamespace("content")
35 public class ContentVideoView extends FrameLayout implements MediaController.MediaPlayerControl,
36         SurfaceHolder.Callback, View.OnTouchListener, View.OnKeyListener, ViewAndroidDelegate  {
37 
38 
39     private static final String TAG = "ContentVideoView";
40 
41     /* Do not change these values without updating their counterparts
42      * in include/media/mediaplayer.h!
43      */
44     private static final int MEDIA_NOP = 0; // interface test message
45     private static final int MEDIA_PREPARED = 1;
46     private static final int MEDIA_PLAYBACK_COMPLETE = 2;
47     private static final int MEDIA_BUFFERING_UPDATE = 3;
48     private static final int MEDIA_SEEK_COMPLETE = 4;
49     private static final int MEDIA_SET_VIDEO_SIZE = 5;
50     private static final int MEDIA_ERROR = 100;
51     private static final int MEDIA_INFO = 200;
52 
53     /**
54      * Keep these error codes in sync with the code we defined in
55      * MediaPlayerListener.java.
56      */
57     public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 2;
58     public static final int MEDIA_ERROR_INVALID_CODE = 3;
59 
60     // all possible internal states
61     private static final int STATE_ERROR              = -1;
62     private static final int STATE_IDLE               = 0;
63     private static final int STATE_PLAYING            = 1;
64     private static final int STATE_PAUSED             = 2;
65     private static final int STATE_PLAYBACK_COMPLETED = 3;
66 
67     private SurfaceHolder mSurfaceHolder;
68     private int mVideoWidth;
69     private int mVideoHeight;
70     private int mCurrentBufferPercentage;
71     private int mDuration;
72     private MediaController mMediaController;
73     private boolean mCanPause;
74     private boolean mCanSeekBack;
75     private boolean mCanSeekForward;
76 
77     // Native pointer to C++ ContentVideoView object.
78     private long mNativeContentVideoView;
79 
80     // webkit should have prepared the media
81     private int mCurrentState = STATE_IDLE;
82 
83     // Strings for displaying media player errors
84     private String mPlaybackErrorText;
85     private String mUnknownErrorText;
86     private String mErrorButton;
87     private String mErrorTitle;
88     private String mVideoLoadingText;
89 
90     // This view will contain the video.
91     private VideoSurfaceView mVideoSurfaceView;
92 
93     // Progress view when the video is loading.
94     private View mProgressView;
95 
96     // The ViewAndroid is used to keep screen on during video playback.
97     private ViewAndroid mViewAndroid;
98 
99     private final ContentVideoViewClient mClient;
100 
101     private class VideoSurfaceView extends SurfaceView {
102 
VideoSurfaceView(Context context)103         public VideoSurfaceView(Context context) {
104             super(context);
105         }
106 
107         @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)108         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
109             int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
110             int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
111             if (mVideoWidth > 0 && mVideoHeight > 0) {
112                 if (mVideoWidth * height  > width * mVideoHeight) {
113                     height = width * mVideoHeight / mVideoWidth;
114                 } else if (mVideoWidth * height  < width * mVideoHeight) {
115                     width = height * mVideoWidth / mVideoHeight;
116                 }
117             }
118             setMeasuredDimension(width, height);
119         }
120     }
121 
122     private static class ProgressView extends LinearLayout {
123 
124         private final ProgressBar mProgressBar;
125         private final TextView mTextView;
126 
ProgressView(Context context, String videoLoadingText)127         public ProgressView(Context context, String videoLoadingText) {
128             super(context);
129             setOrientation(LinearLayout.VERTICAL);
130             setLayoutParams(new LinearLayout.LayoutParams(
131                     LinearLayout.LayoutParams.WRAP_CONTENT,
132                     LinearLayout.LayoutParams.WRAP_CONTENT));
133             mProgressBar = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge);
134             mTextView = new TextView(context);
135             mTextView.setText(videoLoadingText);
136             addView(mProgressBar);
137             addView(mTextView);
138         }
139     }
140 
141     private static class FullScreenMediaController extends MediaController {
142 
143         View mVideoView;
144 
FullScreenMediaController(Context context, View video)145         public FullScreenMediaController(Context context, View video) {
146             super(context);
147             mVideoView = video;
148         }
149 
150         @Override
show()151         public void show() {
152             super.show();
153             if (mVideoView != null) {
154                 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
155             }
156         }
157 
158         @Override
hide()159         public void hide() {
160             if (mVideoView != null) {
161                 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
162             }
163             super.hide();
164         }
165     }
166 
167     private final Runnable mExitFullscreenRunnable = new Runnable() {
168         @Override
169         public void run() {
170             exitFullscreen(true);
171         }
172     };
173 
ContentVideoView(Context context, long nativeContentVideoView, ContentVideoViewClient client)174     private ContentVideoView(Context context, long nativeContentVideoView,
175             ContentVideoViewClient client) {
176         super(context);
177         mNativeContentVideoView = nativeContentVideoView;
178         mViewAndroid = new ViewAndroid(new WindowAndroid(context.getApplicationContext()), this);
179         mClient = client;
180         initResources(context);
181         mCurrentBufferPercentage = 0;
182         mVideoSurfaceView = new VideoSurfaceView(context);
183         setBackgroundColor(Color.BLACK);
184         showContentVideoView();
185         setVisibility(View.VISIBLE);
186         mClient.onShowCustomView(this);
187     }
188 
initResources(Context context)189     private void initResources(Context context) {
190         if (mPlaybackErrorText != null) return;
191         mPlaybackErrorText = context.getString(
192                 org.chromium.content.R.string.media_player_error_text_invalid_progressive_playback);
193         mUnknownErrorText = context.getString(
194                 org.chromium.content.R.string.media_player_error_text_unknown);
195         mErrorButton = context.getString(
196                 org.chromium.content.R.string.media_player_error_button);
197         mErrorTitle = context.getString(
198                 org.chromium.content.R.string.media_player_error_title);
199         mVideoLoadingText = context.getString(
200                 org.chromium.content.R.string.media_player_loading_video);
201     }
202 
showContentVideoView()203     private void showContentVideoView() {
204         FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
205                 ViewGroup.LayoutParams.MATCH_PARENT,
206                 ViewGroup.LayoutParams.MATCH_PARENT,
207                 Gravity.CENTER);
208         this.addView(mVideoSurfaceView, layoutParams);
209         View progressView = mClient.getVideoLoadingProgressView();
210         if (progressView != null) {
211             mProgressView = progressView;
212         } else {
213             mProgressView = new ProgressView(getContext(), mVideoLoadingText);
214         }
215         this.addView(mProgressView, new FrameLayout.LayoutParams(
216                 ViewGroup.LayoutParams.WRAP_CONTENT,
217                 ViewGroup.LayoutParams.WRAP_CONTENT,
218                 Gravity.CENTER));
219         mVideoSurfaceView.setZOrderOnTop(true);
220         mVideoSurfaceView.setOnKeyListener(this);
221         mVideoSurfaceView.setOnTouchListener(this);
222         mVideoSurfaceView.getHolder().addCallback(this);
223         mVideoSurfaceView.setFocusable(true);
224         mVideoSurfaceView.setFocusableInTouchMode(true);
225         mVideoSurfaceView.requestFocus();
226     }
227 
228     @CalledByNative
onMediaPlayerError(int errorType)229     public void onMediaPlayerError(int errorType) {
230         Log.d(TAG, "OnMediaPlayerError: " + errorType);
231         if (mCurrentState == STATE_ERROR || mCurrentState == STATE_PLAYBACK_COMPLETED) {
232             return;
233         }
234 
235         // Ignore some invalid error codes.
236         if (errorType == MEDIA_ERROR_INVALID_CODE) {
237             return;
238         }
239 
240         mCurrentState = STATE_ERROR;
241         if (mMediaController != null) {
242             mMediaController.hide();
243         }
244 
245         /* Pop up an error dialog so the user knows that
246          * something bad has happened. Only try and pop up the dialog
247          * if we're attached to a window. When we're going away and no
248          * longer have a window, don't bother showing the user an error.
249          *
250          * TODO(qinmin): We need to review whether this Dialog is OK with
251          * the rest of the browser UI elements.
252          */
253         if (getWindowToken() != null) {
254             String message;
255 
256             if (errorType == MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
257                 message = mPlaybackErrorText;
258             } else {
259                 message = mUnknownErrorText;
260             }
261 
262             try {
263                 new AlertDialog.Builder(getContext())
264                     .setTitle(mErrorTitle)
265                     .setMessage(message)
266                     .setPositiveButton(mErrorButton,
267                             new DialogInterface.OnClickListener() {
268                         @Override
269                         public void onClick(DialogInterface dialog, int whichButton) {
270                             /* Inform that the video is over.
271                              */
272                             onCompletion();
273                         }
274                     })
275                     .setCancelable(false)
276                     .show();
277             } catch (RuntimeException e) {
278                 Log.e(TAG, "Cannot show the alert dialog, error message: " + message, e);
279             }
280         }
281     }
282 
283     @CalledByNative
onVideoSizeChanged(int width, int height)284     private void onVideoSizeChanged(int width, int height) {
285         mVideoWidth = width;
286         mVideoHeight = height;
287         // This will trigger the SurfaceView.onMeasure() call.
288         mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
289     }
290 
291     @CalledByNative
onBufferingUpdate(int percent)292     private void onBufferingUpdate(int percent) {
293         mCurrentBufferPercentage = percent;
294     }
295 
296     @CalledByNative
onPlaybackComplete()297     private void onPlaybackComplete() {
298         onCompletion();
299     }
300 
301     @CalledByNative
onUpdateMediaMetadata( int videoWidth, int videoHeight, int duration, boolean canPause, boolean canSeekBack, boolean canSeekForward)302     private void onUpdateMediaMetadata(
303             int videoWidth,
304             int videoHeight,
305             int duration,
306             boolean canPause,
307             boolean canSeekBack,
308             boolean canSeekForward) {
309         mProgressView.setVisibility(View.GONE);
310         mDuration = duration;
311         mCanPause = canPause;
312         mCanSeekBack = canSeekBack;
313         mCanSeekForward = canSeekForward;
314         mCurrentState = isPlaying() ? STATE_PLAYING : STATE_PAUSED;
315         if (mMediaController != null) {
316             mMediaController.setEnabled(true);
317             // If paused , should show the controller for ever.
318             if (isPlaying())
319                 mMediaController.show();
320             else
321                 mMediaController.show(0);
322         }
323 
324         onVideoSizeChanged(videoWidth, videoHeight);
325     }
326 
327     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)328     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
329         mVideoSurfaceView.setFocusable(true);
330         mVideoSurfaceView.setFocusableInTouchMode(true);
331         if (isInPlaybackState() && mMediaController != null) {
332             mMediaController.show();
333         }
334     }
335 
336     @Override
surfaceCreated(SurfaceHolder holder)337     public void surfaceCreated(SurfaceHolder holder) {
338         mSurfaceHolder = holder;
339         openVideo();
340     }
341 
342     @Override
surfaceDestroyed(SurfaceHolder holder)343     public void surfaceDestroyed(SurfaceHolder holder) {
344         if (mNativeContentVideoView != 0) {
345             nativeSetSurface(mNativeContentVideoView, null);
346         }
347         mSurfaceHolder = null;
348         post(mExitFullscreenRunnable);
349     }
350 
setMediaController(MediaController controller)351     private void setMediaController(MediaController controller) {
352         if (mMediaController != null) {
353             mMediaController.hide();
354         }
355         mMediaController = controller;
356         attachMediaController();
357     }
358 
attachMediaController()359     private void attachMediaController() {
360         if (mMediaController != null) {
361             mMediaController.setMediaPlayer(this);
362             mMediaController.setAnchorView(mVideoSurfaceView);
363             mMediaController.setEnabled(false);
364         }
365     }
366 
367     @CalledByNative
openVideo()368     private void openVideo() {
369         if (mSurfaceHolder != null) {
370             mCurrentState = STATE_IDLE;
371             mCurrentBufferPercentage = 0;
372             setMediaController(new FullScreenMediaController(getContext(), this));
373             if (mNativeContentVideoView != 0) {
374                 nativeUpdateMediaMetadata(mNativeContentVideoView);
375                 nativeSetSurface(mNativeContentVideoView,
376                         mSurfaceHolder.getSurface());
377             }
378         }
379     }
380 
onCompletion()381     private void onCompletion() {
382         mCurrentState = STATE_PLAYBACK_COMPLETED;
383         if (mMediaController != null) {
384             mMediaController.hide();
385         }
386     }
387 
388     @Override
onTouch(View v, MotionEvent event)389     public boolean onTouch(View v, MotionEvent event) {
390         if (isInPlaybackState() && mMediaController != null &&
391                 event.getAction() == MotionEvent.ACTION_DOWN) {
392             toggleMediaControlsVisiblity();
393         }
394         return true;
395     }
396 
397     @Override
onTrackballEvent(MotionEvent ev)398     public boolean onTrackballEvent(MotionEvent ev) {
399         if (isInPlaybackState() && mMediaController != null) {
400             toggleMediaControlsVisiblity();
401         }
402         return false;
403     }
404 
405     @Override
onKey(View v, int keyCode, KeyEvent event)406     public boolean onKey(View v, int keyCode, KeyEvent event) {
407         boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
408                                      keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
409                                      keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
410                                      keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
411                                      keyCode != KeyEvent.KEYCODE_CALL &&
412                                      keyCode != KeyEvent.KEYCODE_MENU &&
413                                      keyCode != KeyEvent.KEYCODE_SEARCH &&
414                                      keyCode != KeyEvent.KEYCODE_ENDCALL;
415         if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
416             if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
417                     keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
418                 if (isPlaying()) {
419                     pause();
420                     mMediaController.show();
421                 } else {
422                     start();
423                     mMediaController.hide();
424                 }
425                 return true;
426             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
427                 if (!isPlaying()) {
428                     start();
429                     mMediaController.hide();
430                 }
431                 return true;
432             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
433                     || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
434                 if (isPlaying()) {
435                     pause();
436                     mMediaController.show();
437                 }
438                 return true;
439             } else {
440                 toggleMediaControlsVisiblity();
441             }
442         } else if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
443             exitFullscreen(false);
444             return true;
445         } else if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_SEARCH) {
446             return true;
447         }
448         return super.onKeyDown(keyCode, event);
449     }
450 
toggleMediaControlsVisiblity()451     private void toggleMediaControlsVisiblity() {
452         if (mMediaController.isShowing()) {
453             mMediaController.hide();
454         } else {
455             mMediaController.show();
456         }
457     }
458 
isInPlaybackState()459     private boolean isInPlaybackState() {
460         return (mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE);
461     }
462 
463     @Override
start()464     public void start() {
465         if (isInPlaybackState()) {
466             if (mNativeContentVideoView != 0) {
467                 nativePlay(mNativeContentVideoView);
468             }
469             mCurrentState = STATE_PLAYING;
470         }
471     }
472 
473     @Override
pause()474     public void pause() {
475         if (isInPlaybackState()) {
476             if (isPlaying()) {
477                 if (mNativeContentVideoView != 0) {
478                     nativePause(mNativeContentVideoView);
479                 }
480                 mCurrentState = STATE_PAUSED;
481             }
482         }
483     }
484 
485     // cache duration as mDuration for faster access
486     @Override
getDuration()487     public int getDuration() {
488         if (isInPlaybackState()) {
489             if (mDuration > 0) {
490                 return mDuration;
491             }
492             if (mNativeContentVideoView != 0) {
493                 mDuration = nativeGetDurationInMilliSeconds(mNativeContentVideoView);
494             } else {
495                 mDuration = 0;
496             }
497             return mDuration;
498         }
499         mDuration = -1;
500         return mDuration;
501     }
502 
503     @Override
getCurrentPosition()504     public int getCurrentPosition() {
505         if (isInPlaybackState() && mNativeContentVideoView != 0) {
506             return nativeGetCurrentPosition(mNativeContentVideoView);
507         }
508         return 0;
509     }
510 
511     @Override
seekTo(int msec)512     public void seekTo(int msec) {
513         if (mNativeContentVideoView != 0) {
514             nativeSeekTo(mNativeContentVideoView, msec);
515         }
516     }
517 
518     @Override
isPlaying()519     public boolean isPlaying() {
520         return mNativeContentVideoView != 0 && nativeIsPlaying(mNativeContentVideoView);
521     }
522 
523     @Override
getBufferPercentage()524     public int getBufferPercentage() {
525         return mCurrentBufferPercentage;
526     }
527 
528     @Override
canPause()529     public boolean canPause() {
530         return mCanPause;
531     }
532 
533     @Override
canSeekBackward()534     public boolean canSeekBackward() {
535         return mCanSeekBack;
536     }
537 
538     @Override
canSeekForward()539     public boolean canSeekForward() {
540         return mCanSeekForward;
541     }
542 
543     @Override
getAudioSessionId()544     public int getAudioSessionId() {
545         return 0;
546     }
547 
548     @CalledByNative
createContentVideoView( Context context, long nativeContentVideoView, ContentVideoViewClient client)549     private static ContentVideoView createContentVideoView(
550             Context context, long nativeContentVideoView, ContentVideoViewClient client) {
551         ThreadUtils.assertOnUiThread();
552         // The context needs be Activity to create the ContentVideoView correctly.
553         if (!(context instanceof Activity)) {
554             Log.w(TAG, "Wrong type of context, can't create fullscreen video");
555             return null;
556         }
557         return new ContentVideoView(context, nativeContentVideoView, client);
558     }
559 
removeMediaController()560     private void removeMediaController() {
561         if (mMediaController != null) {
562             mMediaController.setEnabled(false);
563             mMediaController.hide();
564             mMediaController = null;
565         }
566     }
567 
removeSurfaceView()568     public void removeSurfaceView() {
569         removeView(mVideoSurfaceView);
570         removeView(mProgressView);
571         mVideoSurfaceView = null;
572         mProgressView = null;
573     }
574 
exitFullscreen(boolean relaseMediaPlayer)575     public void exitFullscreen(boolean relaseMediaPlayer) {
576         destroyContentVideoView(false);
577         if (mNativeContentVideoView != 0) {
578             nativeExitFullscreen(mNativeContentVideoView, relaseMediaPlayer);
579             mNativeContentVideoView = 0;
580         }
581     }
582 
583     /**
584      * This method shall only be called by native and exitFullscreen,
585      * To exit fullscreen, use exitFullscreen in Java.
586      */
587     @CalledByNative
destroyContentVideoView(boolean nativeViewDestroyed)588     private void destroyContentVideoView(boolean nativeViewDestroyed) {
589         if (mVideoSurfaceView != null) {
590             removeMediaController();
591             removeSurfaceView();
592             setVisibility(View.GONE);
593 
594             // To prevent re-entrance, call this after removeSurfaceView.
595             mClient.onDestroyContentVideoView();
596         }
597         if (nativeViewDestroyed) {
598             mNativeContentVideoView = 0;
599         }
600     }
601 
getContentVideoView()602     public static ContentVideoView getContentVideoView() {
603         return nativeGetSingletonJavaContentVideoView();
604     }
605 
606     @Override
onTouchEvent(MotionEvent ev)607     public boolean onTouchEvent(MotionEvent ev) {
608         return true;
609     }
610 
611     @Override
onKeyDown(int keyCode, KeyEvent event)612     public boolean onKeyDown(int keyCode, KeyEvent event) {
613         if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
614             exitFullscreen(false);
615             return true;
616         }
617         return super.onKeyDown(keyCode, event);
618     }
619 
620     @Override
acquireAnchorView()621     public View acquireAnchorView() {
622         View anchorView = new View(getContext());
623         addView(anchorView);
624         return anchorView;
625     }
626 
627     @Override
setAnchorViewPosition(View view, float x, float y, float width, float height)628     public void setAnchorViewPosition(View view, float x, float y, float width, float height) {
629         Log.e(TAG, "setAnchorViewPosition isn't implemented");
630     }
631 
632     @Override
releaseAnchorView(View anchorView)633     public void releaseAnchorView(View anchorView) {
634         removeView(anchorView);
635     }
636 
637     @CalledByNative
getNativeViewAndroid()638     private long getNativeViewAndroid() {
639         return mViewAndroid.getNativePointer();
640     }
641 
nativeGetSingletonJavaContentVideoView()642     private static native ContentVideoView nativeGetSingletonJavaContentVideoView();
nativeExitFullscreen(long nativeContentVideoView, boolean relaseMediaPlayer)643     private native void nativeExitFullscreen(long nativeContentVideoView,
644             boolean relaseMediaPlayer);
nativeGetCurrentPosition(long nativeContentVideoView)645     private native int nativeGetCurrentPosition(long nativeContentVideoView);
nativeGetDurationInMilliSeconds(long nativeContentVideoView)646     private native int nativeGetDurationInMilliSeconds(long nativeContentVideoView);
nativeUpdateMediaMetadata(long nativeContentVideoView)647     private native void nativeUpdateMediaMetadata(long nativeContentVideoView);
nativeGetVideoWidth(long nativeContentVideoView)648     private native int nativeGetVideoWidth(long nativeContentVideoView);
nativeGetVideoHeight(long nativeContentVideoView)649     private native int nativeGetVideoHeight(long nativeContentVideoView);
nativeIsPlaying(long nativeContentVideoView)650     private native boolean nativeIsPlaying(long nativeContentVideoView);
nativePause(long nativeContentVideoView)651     private native void nativePause(long nativeContentVideoView);
nativePlay(long nativeContentVideoView)652     private native void nativePlay(long nativeContentVideoView);
nativeSeekTo(long nativeContentVideoView, int msec)653     private native void nativeSeekTo(long nativeContentVideoView, int msec);
nativeSetSurface(long nativeContentVideoView, Surface surface)654     private native void nativeSetSurface(long nativeContentVideoView, Surface surface);
655 }
656