视频播放器之遇到问题篇(写的不好不对的地方欢迎指出)

                                                        视频播放器之遇到问题篇

---------------------------------------------------------------------------------------------------------------------------

遇到问题1:

       当播放公司服务器视频,在模拟器2.1系统下无问题播放,但在模拟器2.2系统下当执行到MediaPlayer.prepaer();这个方法时会抛出IOException异常?

      

解决思路:

       首先去查看了Android里面MediaPlayer.prepaer();的源代码,看看在什么情况在会抛出IO异常,但是它的MediaPlayer.java里的prepaer()方法是 native的,看MeidaPlayer.cpp也没具体看明白.后来查看Android Supported Media Formats发现其支持视频编码是Baseline Profile (BP),而播放视频通过MediaInfo查看其视频编码是Baseline@3。后来连接手机调试,使用MOTO Defy 2.2无法播放,但放到HTCG7 2.2及平板电脑上可正常播放,据此推断,可能是由于厂家定制底层和内核的时候,根据需求增减了一部分支持的视频编码。

 

---------------------------------------------------------------------------------------------------------------------------

遇到问题2

       正常情况下如果视频处于started状态直接调用MediaPlayer.seekTo()跳转指定时间没有问题,但是如果处于paused状态调用MediaPlayer.seekTo()方法后,此时开始调用start()方法会出现error,,onError(MediaPlayer mp, int what, int extra)中返回的是

07-1916:05:29.499: INFO/System.out(7763):  whatis 100   extra  0

100代表的是MEDIA_ERROR_SERVER_DIED

extra  0没搞懂是什么,后来查看自带的播放器也存在这个问题,视频暂停,然后拖动进度条,在开始会发生错误。

 

解决办法:

       尝试方法1:

1在拖动进度条监听器的onStopTrackingTouch()方法中首先检测视频是否正在播放(mMediaPlayer.isPlaying()==true),

2如果拖动前是暂停状态的话,就先start进入started状态,

3然后在调用seekTo()方法,测试后还是会问题,错误如下

 

07-1916:54:05.569: ERROR/ProfileVideoFrameDrops(8984): PVMediaOutputNodePort 1Frames dropped at 1

07-1916:54:05.960: ERROR/TI_Video_Decoder(8984): 2273 VIDDEC_HandleCommandFlush DSPflushed without processing SPS/PPS. saving first buffer

07-1916:54:05.983: ERROR/MediaPlayer(8959): internal/external state mismatchcorrected

 

onError(MediaPlayermp, int what, int extra)中返回的是

07-1916:54:05.983: INFO/System.out(8959): what is 100      extra 0

 

然后进入了OnCompletionListener中的onCompletion方法

 

然后onError r(MediaPlayer mp, int what, intextra)继续返回

07-1916:54:06.092: INFO/System.out(8959): what is -38  extra 0

说明此方法行不通。

 

       尝试方法2:

       1 和第一步一样,看是否在播放中

       2 如果处于paused状态,调用reset()方法,

       3然后重新调用prepare()方法准备

       4开始seekTo();或者先start(),然后seekTo();

 

       出现的问题是:

       onError(MediaPlayer mp, int what, intextra)中返回的是

       07-19 17:10:17.749:INFO/System.out(9472): what is -38     extra  0

 

       尝试方法3:

       1 和第一步一样,看是否在播放中

       2如果处于paused状态,调用stop()方法,

       3然后重新调用prepare()方法准备

       4开始seekTo();

       这种方法是我暂时尝试到了能行得通的方法,但是有个小瑕疵,就是seekTo()后自动播放了,这时调用pause()也停不了,或者先start(),然后pause()也不行。不知道是什么原因,查看log发现当跳动时的错误

07-1920:17:20.108: ERROR/TI_LCML(11015): 507 :: Exiting Init_DSPSubSystem

07-1920:17:20.881: ERROR/ProfileVideoFrameDrops(11015): PVMediaOutputNodePort 5Frames dropped at 6

---------------------------------------------------------------------------------------------------------------------------

 

遇到问题3

       获取PopupWindow上的按钮ID 例如ImageButton playButton = (ImageButton)findViewById(R.id.play);一直出现空指针异常。

 

解决办法:

       这时候findViewById(R.id.play)是当前View的,而不是PopupWindow里的,应该修改成为

ImageButtonplayButton = (ImageButton) vPopupWindow.findViewById(R.id.play);

 

---------------------------------------------------------------------------------------------------------------------------

遇到问题4

       在实验PopupWindow时,把seekBar和开始按钮放到PopupWindow上,但是弹出PopupWindow后,点击PopupWindow外部其他控件时,PopupWindow无法消失,焦点一直在PopupWindow上.

 

解决办法:

       网上查看相关资料后。发现是因为没有给PopupWindow设置背景图片,使用下面这个方法

popup.setBackgroundDrawable(getResources().getDrawable(R.drawable.videoplayer_bg));

后问题解决。

      

---------------------------------------------------------------------------------------------------------------------------

遇到问题5

       想在视频播放中使用OnGestureListener识别用户手势,来做到不使用控制台快捷控制一些常用的例如快进倒退调声等,但是实验时无法进入android.view.GestureDetector.OnGestureListener里的public boolean onFling()函数.因此没法检测手势

解决办法:

       把默认生成的onDown方法返回值改成 return true;

       public boolean onDown(MotionEvent e)

    {

       // TODO Auto-generatedmethod stub

       return true;

    }

----------------------------------------------------------------------------------

遇到问题6

       使用更新时间线程和打开控制台一定时间后自动隐藏控制台线程时,如果处于线程延时时间内横竖屏转换时,转变后会发生错误导致崩溃.错误如下:

07-2017:13:13.780: WARN/dalvikvm(12151): threadid=1: thread exiting with uncaughtexception (group=0x400208b0)

07-2017:13:13.803: ERROR/AndroidRuntime(12151): java.lang.IllegalArgumentException:View not attached to window manager

 

解决办法

       原因估计是在横竖屏转换时Activity重载时没有注销线程里的Runnable

       隐藏控制台线程在run()方法结尾执行注销线程里的runnable

       dismissHandler.removeCallbacks(dismissRunnable);

在Activity里的onDestroy()方法里

执行

if(updateHandle!=null)

       {

           updateHandle.removeCallbacks(updateRunnable);

           releaseMediaPlayer();

       }

       if(dismissHandler!=null)

       {

           dismissHandler.removeCallbacks(dismissRunnable);

       }

 

---------------------------------------------------------------------------------------------------------------------------

遇到问题7

       播放视频中,如果中途返回桌面,处理完外部事件在回到播放器,会出现无法继续退出前的时间点继续播放。错误是播放器线程死亡。

 

解决办法:

       首先在activity的onPause()方法里保存时间断点,这里我用一个static int userPauseTime来保存。

       protected void onPause()

    {

       userPauseTime=mMediaPlayer.getCurrentPosition();

       mMediaPlayer.pause();

       System.out.println("onPause");

       super.onPause();

    }

       当用户处理完其他事件重新进入activity,程序执行流程根据我的跟踪是这样的,

onResume()->surfaceCreated()->initMedia(media初始化)->onPrepared()

    如果在onResume()直接seekTo()是不行的,因为这个时候貌似mediaplayer已经挂掉了,所以在surfaceCreated()里需要重新如下面这样new一个 mediaplayer,然后重新初始化

    if (mMediaPlayer != null) {

            mMediaPlayer.reset();

            mMediaPlayer.release();

            mMediaPlayer = null;

        }

       // 对mMedia进行相关准备工作

       try{

           mMediaPlayer = new MediaPlayer();

           mMediaPlayer.setDataSource(path);

           mMediaPlayer.setDisplay(holder);

           mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

           mMediaPlayer.prepareAsync();

       }

       catch (Exception e) {

           // TODO: handleexception

           System.out.println("e"+e);

       }

             

       initMediaAllListener();

       mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

 

    然后在onPrepared()方法中,判断是否用户之前有中断的时间,如果没有的话,应该属于第一次启动本视频,如果有的话 就调用 seekTo()跳转到之前的时间

    public void onPrepared(MediaPlayer mediaplayer)

    {

       Log.d(TAG, "onPreparedcalled");

       mIsVideoReadyToBePlayed = true;

       if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown)

       {

//         startVideoPlayback();

       }

       if(userPauseTime==0)

       {

           startVideoPlayback();

           System.out.println("startVideoPlayback();");

       }

       else

       {         

           mMediaPlayer.seekTo(userPauseTime);

           mMediaPlayer.start();

           startProgressUpdate();

           userPauseTime=0;

           System.out.println("userPauseTime!=0");

       }

    }

---------------------------------------------------------------------------------------------------------------------------

遇到问题8

       不清楚onInfo和onError里打印出来的 what 和 extra 代表什么意思,因此无法辨别mediaplayer出了什么错误.

 

解决办法:

    总结如下

1.onInfo里 what= 1extra=44 时,代表着可以连接本地视频文件或者连接目标服务器流文件成功。

2.OnInfo里 what= 1extra=26 时, 代表无法找到连接本地视频文件或者连接目标服务器。接着会

    onError 里会打印出 INFO/System.out(5514): what is 1 extra -4,然后继续打印出

    onError INFO/System.out(5514):what is -38   extra  0

    只要mediaplayer出了错误 最后都是onError 打印出 what is -38   extra  0

    但是API文档中并未找到详细说明或给出对应的错误列表...

       经过研究和网上资料的收集,暂总结如下:

       在这个网站下可以查到如下内容

       http://android.git.kernel.org/?p=platform/external/opencore.git;a=blob;f=pvmi/pvmf/include/pvmf_return_codes.h;h=ed5a2539ca85ae60425229be41646b6bd7d9389c;hb=HEAD

        /*

   *DRM clock is not available or cannot be read

   */

   const PVMFStatus PVMFErrDrmClockError = (-38);

  /*

   *Return code for pending completion

   */

  const PVMFStatus PVMFPending = 0;

    DRM指的是内容数字版权加密保护技术。 由于数字化信息的特点决定了必须有另一种独特的技术,来加强保护这些数字化的音视频节目内容的版权。

3播放正在缓冲时,无线网络断开

       07-2510:25:16.647: ERROR/MediaPlayer(8789): error (1, -17)既 what is 1, extra is -17

 

 

 

07-25 10:55:17.436: INFO/System.out(12355): whatis 1extra  31

 

07-27 10:23:42.040: INFO/System.out(14344):onInfo is 1     extra is28

 

---------------------------------------------------------------------------------------------------------------------------------------------

遇到问题9

       横竖屏速度慢,而且会重新开始播放视频。如何能加快转换速度,并且不间断播放?

 

解决办法:

       转换速度慢并且重新播放是由于横竖屏转换时调用了activity里的onCreate()方法导致重新加载了,如果不想自动转换时调用onCreate()则在AndroidMaifest.xml中相应的activity下加入如下语句:

".MediaPlayerDemo_Video"

       android:configChanges="orientation|keyboardHidden"

       >

 

---------------------------------------------------------------------------------------------------------------------------------------------

 

遇到问题10

       调整音量时,会显示出系统自带的音量调整UI ,可能会遮挡住视频,如何才能不显示自带的音量UI?

 

解决办法:

       在调整音量时,是使用下面这条语句:

       audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,

                     AudioManager.ADJUST_LOWER,AudioManager.FLAG_SHOW_UI);

    最后一个参数指的就是需不需要显示出音量UI,如果设置为0 ,就不会显示了。

 

---------------------------------------------------------------------------------------------------------------------------------------------

遇到问题11

       在播放网络流媒体时,如果网速太慢,根据缓冲进度来给用户正在缓冲的提示?

 

解决办法:

    第一种,在onBufferingUpdate()里面跟踪视频正在播放时间,如果与上一次时间相同,则弹出缓冲提示如下

           int CurrentTime =mMediaPlayer.getCurrentPosition();

           if(lastBufferTime==CurrentTime)

           {

              if(bufferAlertDialog.isShowing()==false)

              {

                 

                  bufferAlertDialog.show();

                  System.out.println("bufferAlertDialog.show();");

              }

           }

           else

           {

              if(bufferAlertDialog.isShowing()==true)

              {

                  bufferAlertDialog.dismiss();

                  bufferAlertDialog=null;

                  System.out.println("bufferAlertDialog.dismiss();");

              }

           }

           if(mMediaPlayer!=null)

        {

           if(mMediaPlayer.isPlaying()==true)

           {

               lastBufferTime=CurrentTime;//正在播放才更新上一次缓冲时间 防止用户暂停时也更新

           }

       }

    但是这样有些瑕疵 ,首先是每次先是画面停顿后2秒-3秒左右后才弹出缓冲中,这是由于onBuffer自己调用的时间是2s左右。

    其次是可以播放,但是播放起来很卡,这种方法暂时不能给出任何提示。

---------------------------------------------------------------------------------------------------------------------------------------------

遇到问题12

    使用ProgressDialog进行缓冲提示时,第一次ProgressDialog上的进度条会旋转,但在播放中进度条不会旋转。

 

解决办法:

 

    在每次onBuffer里面重新生成 ProgressDialog,

           if(bufferAlertDialog==null)

           {

              initBuffering();

           }

    然后在每次dismiss后赋值null,

               if(bufferAlertDialog.isShowing()==true)

              {

                  bufferAlertDialog.dismiss();

                  bufferAlertDialog=null;

              }

 

    注意: 这里不能使用 dismiss()不能改成dialog.hide()方法,经过测试,hide方法后 isShowing()返回的一直是 true ,虽然在手机上看不见了,但是依然是 isShowing

 

 

---------------------------------------------------------------------------------------------------------------------------------------------

遇到问题13

    如何在屏幕双击后自动切换播放视频原始尺寸或全屏显示?

 

解决办法:

    可以通过设置SurfaceView的参数来调整显示大小,如下

    public void setVideoScale(int width, int height)

    {

       LayoutParams lp = mPreview.getLayoutParams();

       lp.height = height;

       lp.width = width;

       mPreview.setLayoutParams(lp);

    }

width和height 如果是视频默认尺寸的话就是

int videoWidth = mMediaPlayer.getVideoWidth();

int videoHeight = mMediaPlayer.getVideoHeight();

 

全屏显示时就是

Display display =getWindowManager().getDefaultDisplay();

screenHeight = display.getHeight();

screenWidth = display.getWidth();

 

 

 

最后在双击的监听器中

    @Override

    public boolean onDoubleTap(MotionEvent e)

    {

       System.out.println("onDoubleTap");

       if (isFullScreen)

       {

           setVideoScale(SCREEN_DEFAULT);

       } else

       {

           setVideoScale(SCREEN_FULL);

       }

       isFullScreen = !isFullScreen;

       return true;

    }

---------------------------------------------------------------------------------------------------------------------------------------------

遇到问题14

    横竖屏转换时,竖屏全屏播放转换成横屏时,只能显示在左边而且显示一半.

   

解决办法:

    显示在左边是xml中的布局问题,在其修改成

"@+id/surface"

       android:layout_width="fill_parent"

       android:layout_height="fill_parent"

       android:layout_centerInParent="true"

       >

   

    显示一半是由于在转换时没有重新获得屏幕的大小,它就按照你原先的大小重新显示,所以必须在屏幕转换配置监听器里

public voidonConfigurationChanged(Configuration newConfig)中,重新获得屏幕大小,然后重新设置为全屏或默认,如下

    getScreenSize();

    if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)

       {

           setVideoScale(SCREEN_FULL);

           isFullScreen=true;

       else

       {   if(isFullScreen==true)

           {

              setVideoScale(SCREEN_FULL);

              isFullScreen=true;

           }

           else

           {

              setVideoScale(SCREEN_DEFAULT);

              isFullScreen=false;

           }

          

        }

 

---------------------------------------------------------------------------------------------------------------------------------------------

遇到问题15

    在弹出控制台后,如果在控制台自动消失(5s)前,手动消失,然后在触摸,控制台会继续之前5s剩下的秒数后消失,而不是重新开始.

 

解决办法:

    主要缺少手动消失时移除上一次时间runnable;

    原来程序里是这么判断的,在触摸事件里

public boolean onSingleTapUp(MotionEvent e)

{

    if (popup != null)

       {

           if (popup.isShowing()==true)

           {

              pop.dismiss();

           } else

           {

              popup.showAtLocation(findViewById(R.id.surface),

                     Gravity.BOTTOM, 0, -50);

              mViewFlipper.startFlipping();

              startControlBarDismiss(5000);

              System.out.println("startControlBarDismiss(5000);");

           }

       }

}

    经过验证,只有在当前最前端窗体是视频窗口时,触摸时在会触发onSingleTapUp,这是因为是把onTouchListener安装在视频窗口的LinearLayout上,所以手动触摸控制台以外时控制台窗体消失时并不是触发触摸里的pop.dismiss();估计是系统自带的前端窗口消失

    办法是在每次弹出前就检测上一次是否有时间runnable,线程不为空的话就移除runnable,而不是在消失的时候移除

    if(dismissHandler!=null)

    {

       dismissHandler.removeCallbacks(dismissRunnable);

    }  

       popup.showAtLocation(findViewById(R.id.surface),

                     Gravity.BOTTOM, 0, -50);

       mViewFlipper.startFlipping();

       startControlBarDismiss(5000);

       System.out.println("startControlBarDismiss(5000);");

    并在run()方法结束的时候移除runnable 和 赋null;

    If(dismissHandler!=null)

    {

       dismissHandler.removeCallbacks(dismissRunnable);

       dismissHandler=null;

    }

 

---------------------------------------------------------------------------------------------------------------------------------------------

 

遇到问题16

    在控制台里拖动seekBar,seekBar在拖动中一闪一闪的跳回到播放时间

 

解决办法

    这是由于在拖动时更新进度条线程还在一直更新,办法是在seekBar的onStartTrackingTouch()(开始拖动监听器中)首先先移除updaterunnable()

    在拖动停止时,在执行updateHandle.post(updateRunnable);

   

---------------------------------------------------------------------------------------------------------------------------------------------

 

遇到问题17

    在通过上下手势播放时调节亮度中,只能调节一次,每次修改完读取亮度值还是上次的值。

 

解决办法:

   

    原来程序中在onFling()中 每次上下手势时读取上次亮度,然后+30

       setBrightness(getNowBrihtness()+ 30);

       getNowBrihtness();

      

    private void setBrightness(int brightness)

    {

       WindowManager.LayoutParams lp = this.getWindow().getAttributes();

       lp.screenBrightness = Float.valueOf(brightness) * (1f / 255f);

       this.getWindow().setAttributes(lp);

    }

 

    private int getNowBrihtness()

    {

 

       try

       {

           nowBrightnessValue = android.provider.Settings.System.getInt(

                  resolver, Settings.System.SCREEN_BRIGHTNESS);

           System.out.println("nowBrightnessValue" + nowBrightnessValue);

       } catch (Exception e)

       {

           e.printStackTrace();

       }

       return nowBrightnessValue;

    }

       两次打印出来的亮度值是一样的。

    经过资料发现,使用setBrightness()只是对当前activity设置亮度值,而不是设置系统的亮度值,当退出activity就使用回系统的亮度值,而getNowBrihtness()每次是获取系统的亮度值。造成了只能调整一次的假象,其实每次都是调整成了一样的值而已。

    想要保存到系统的亮度值,首先需要设置一下权限

   

然后使用下面这个方法

    private void saveBrightness(int brightness)

    {

       Uri uri = android.provider.Settings.System

              .getUriFor("screen_brightness");

       android.provider.Settings.System.putInt(resolver, "screen_brightness",

              brightness);

       //resolver.registerContentObserver(uri, true, myContentObserver);

       resolver.notifyChange(uri, null);

    }

这样子每次调整完这个方法保存到系统就可以了,或者可以在程序里一个变量初始化时读取系统亮度值,然后更改窗体亮度完也对这个变量修改,这种是不会对系统的亮度造成任何影响。

 

---------------------------------------------------------------------------------------------------------------------------------------------

你可能感兴趣的:(Android开发)