zoukankan      html  css  js  c++  java
  • android播放器:mediaplayer

    近几天的主要问题在于播放器。原来采用的方案最终发现存在问题无法实施,只好临时替换。想起最开始就曾经想用的android提供的VideoView和MediaPlayer组件,开始替换,然后就是一堆问题。网上查到的大部分资料都没有解决问题,无意中搜到这篇文章,感觉还不错的问题总结,希望能帮我解决问题吧,也希望对正在学习的人能有所帮助。

    主要内容来自于(原文网址):http://www.boyunjian.com/do/article/snapshot.do?uid=3453185252600635258,原文标题:视频播放器之遇到问题篇 ,转载请注明。(不知道是否本身就是被转载,已经找不到作者的名字了)

    内容如下:

     视频播放器之遇到问题篇 
    --------------------------------------------------------------------------------------------------------------------------- 
    遇到问题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下加入如下语句: 
    <activity android:name= ".MediaPlayerDemo_Video" 
           android:configChanges= "orientation|keyboardHidden" 
           ></activity> 
      
    --------------------------------------------------------------------------------------------------------------------------------------------- 
      
    遇到问题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中的布局问题,在其修改成 
    <SurfaceView android:id= "@+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()每次是获取系统的亮度值。造成了只能调整一次的假象,其实每次都是调整成了一样的值而已。 
        想要保存到系统的亮度值,首先需要设置一下权限 
        
    <uses-permissionandroid:name="android.permission.WRITE_SETTINGS"></uses-permission> 
    然后使用下面这个方法 
        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 ); 
        } 
    这样子每次调整完这个方法保存到系统就可以了,或者可以在程序里一个变量初始化时读取系统亮度值,然后更改窗体亮度完也对这个变量修改,这种是不会对系统的亮度造成任何影响。 
     
  • 相关阅读:
    hi.baidu.com 百度流量统计
    Autofac is designed to track and dispose of resources for you.
    IIS Manager could not load type for module provider 'SharedConfig' that is declared in administration.config
    How to create and manage configuration backups in Internet Information Services 7.0
    定制swagger的UI
    NSwag在asp.net web api中的使用,基于Global.asax
    NSwag Tutorial: Integrate the NSwag toolchain into your ASP.NET Web API project
    JS变量对象详解
    JS执行上下文(执行环境)详细图解
    JS内存空间详细图解
  • 原文地址:https://www.cnblogs.com/Dionexin/p/6169307.html
Copyright © 2011-2022 走看看