Android MediaPlayer 在 Android 4.2.2 上播放视频时闪烁

Posted

技术标签:

【中文标题】Android MediaPlayer 在 Android 4.2.2 上播放视频时闪烁【英文标题】:Android MediaPlayer flickers when playing videos on Android 4.2.2 【发布时间】:2017-02-06 00:45:37 【问题描述】:

我在使用 android MediaPlayer 类时遇到了一个奇怪的问题。我正在使用 MediaPlayer 在 VideoView 中播放视频。这适用于所有设备,除了带有 Android 4.2.2 的三星 GT-I8200N。好吧,我实际上听说过视频无法在某些三星 4.1.X 设备上播放的报道,但我无法获得这些设备之一。我在 Google Android Emulators 中没有收到任何错误。

更多细节:采用 Android 4.2.2 的三星 GT-I8200N 能够播放一个片段中的视频,但不能播放另一个片段中的视频,即使使用相同的代码播放视频也是如此。当视频开始时,整个屏幕闪烁黑色,除了视频应该在的空间。

代码如下:

public abstract class AbstractSignVideoFragment extends Fragment 

private static final double MAXMIMUM_VIDEO_HEIGHT_ON_LANDSCAPE = 0.4;
private static final double MAXIMUM_VIDEO_WIDTH_ON_PORTRAIT = 0.8;
private final static String TAG = AbstractSignVideoFragment.class.getSimpleName();
private static final String ANDROID_RESOURCE = "android.resource://";
private static final String SLASH = "/";
private static final String RAW = "raw";
protected VideoView videoView;
protected ProgressBar progressBar;

@SuppressWarnings("BooleanMethodIsAlwaysInverted")
protected boolean isSetupVideoViewSuccessful(final Sign sign, final SOUND sound, final CONTROLS controls) 
    initializeMediaController();
    final String mainActivityPackageName = getActivity().getPackageName();
    final int signIdentifier = getActivity().getResources().getIdentifier(sign.getName(), RAW, mainActivityPackageName);
    if (0 == signIdentifier) 
        return false;
    
    final Uri uri = Uri.parse(ANDROID_RESOURCE + mainActivityPackageName + SLASH + signIdentifier);
    if (!isVideoViewDimensionSetToMatchVideoMetadata(this.videoView, uri)) 
        return false;
    
    this.videoView.setVideoURI(uri);
    this.videoView.requestFocus();
    this.videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() 
        public void onPrepared(MediaPlayer mp) 
            AbstractSignVideoFragment.this.progressBar.setVisibility(View.GONE);
            if (sound.equals(SOUND.OFF)) 
                mp.setVolume(0f, 0f);
            
            AbstractSignVideoFragment.this.videoView.start();
            AbstractSignVideoFragment.this.videoView.setContentDescription(getActivity()
                    .getString(R.string.videoIsPlaying) + ": " + sign.getName());
            Log.d(TAG, String.format("Actual width: %s, Actual height: %s",
                    AbstractSignVideoFragment.this.videoView.getWidth(),
                    AbstractSignVideoFragment.this.videoView.getHeight()));
            // Set the MediaController to null so the controls are not 'popping up'
            // when the video plays for the first time.
            AbstractSignVideoFragment.this.videoView.setMediaController(null);
        
    );
    this.videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() 
        @Override
        public void onCompletion(MediaPlayer mp) 
            if (controls.equals(CONTROLS.SHOW)) 
                initializeMediaController();
            
        
    );
    return true;


private void initializeMediaController() 
    final MediaController mediaController = new MediaController(getActivity(), false);
    mediaController.setAnchorView(this.videoView);
    this.videoView.setMediaController(mediaController);


private boolean isVideoViewDimensionSetToMatchVideoMetadata(VideoView videoView, Uri uri) 
    String metadataVideoWidth;
    String metadataVideoHeight;
    try 
        final MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
        metaRetriever.setDataSource(getActivity(), uri);
        metadataVideoWidth = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
        metadataVideoHeight = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
        metaRetriever.release();
        Validate.notEmpty(metadataVideoWidth);
        Validate.notEmpty(metadataVideoHeight);
     catch (NullPointerException | IllegalArgumentException ex) 
        return false;
    
    final double videoWidth = Double.valueOf(metadataVideoWidth);
    final double videoHeight = Double.valueOf(metadataVideoHeight);
    final double videoRatio = videoWidth / videoHeight;
    Log.d(TAG, String.format("videoWidth: %s, videoHeight: %s, videoRatio: %s", videoWidth, videoHeight, videoRatio));
    boolean isOrientationPortrait = Configuration.ORIENTATION_PORTRAIT == getResources().getConfiguration().orientation;
    int displayHeight = getResources().getDisplayMetrics().heightPixels;
    int displayWidth = getResources().getDisplayMetrics().widthPixels;
    Log.d(TAG, String.format("displayHeight: %s, displayWidth: %s", displayHeight, displayWidth));
    final double desiredVideoWidth, desiredVideoHeight;
    if (isOrientationPortrait) 
        desiredVideoWidth = displayWidth * MAXIMUM_VIDEO_WIDTH_ON_PORTRAIT;
        desiredVideoHeight = 1 / (videoRatio / desiredVideoWidth);
        Log.d(TAG, String.format("OrientationPortrait: desiredVideoWidth: %s, desiredVideoHeight: %s", desiredVideoWidth, desiredVideoHeight));
     else  // orientation is Landscape
        desiredVideoHeight = displayHeight * MAXMIMUM_VIDEO_HEIGHT_ON_LANDSCAPE;
        desiredVideoWidth = desiredVideoHeight * videoRatio;
        Log.d(TAG, String.format("OrientationLandscape: desiredVideoWidth: %s, desiredVideoHeight: %s", desiredVideoWidth, desiredVideoHeight));
    
    final ViewGroup.LayoutParams layoutParams = videoView.getLayoutParams();
    layoutParams.width = (int) desiredVideoWidth;
    layoutParams.height = (int) desiredVideoHeight;
    return true;


public enum SOUND ON, OFF

public enum CONTROLS SHOW, HIDE

这是屏幕闪烁时循环的 logcat 输出:

D/v_gal(115):[tid=275] gcmVERIFY_ARGUMENT 失败:

D/v_gal(115): [tid=275] gcmONERROR: 状态=-1(gcvSTATUS_INVALID_ARGUMENT)@_Blit(2208)

E/v_hwc(115): _Blit 失败: status=-1

D/v_gal(115): [tid=275] gcmONERROR: 状态=-1(gcvSTATUS_INVALID_ARGUMENT)@hwcComposeG2D(615)

E/v_hwc(115):在 hwcComposeG2D 中失败:状态=-1

E/v_hwc(115): _Set(1007): 撰写失败

完整的 logcat 输出可以在这里找到:Github issue (full logcat)

视频查看代码:

< VideoView
android:id="@+id/signTrainerVideoView"
android:layout_
android:layout_
android:layout_below="@+id/signTrainerQuestionText"
android:layout_centerHorizontal="true" />

片段的完整布局 xml 可以在这里找到:layout xml (full fragment)

【问题讨论】:

嗯...VideoView 是一个非常古老的小部件。您是否尝试过使用 TextureView 看到这个link 有什么解决办法吗? 【参考方案1】:

我不确定这是否能解决问题,但我只会报告其中似乎可以改进的地方。

为了实现您的 VideoView 大小:

final ViewGroup.LayoutParams layoutParams = videoView.getLayoutParams();
layoutParams.width = (int) desiredVideoWidth;
layoutParams.height = (int) desiredVideoHeight;
videoView.setLayoutParams(layoutParams); // Input Changes
videoView.getHolder().setSizeFromLayout(); // Apply Changes

此外,根据我的实验,“0dip”大小意味着使用权重参数进行计算,而您似乎没有提供。可能是错误的,但值得一提。

// Add android:weightSum="1" to the sup. Layout
// And set a weight there
<VideoView
            android:id="@+id/signTrainerVideoView"
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:layout_below="@+id/signTrainerQuestionText"
            android:layout_centerHorizontal="true" />

【讨论】:

感谢您的回复。我会尽快检查的。

以上是关于Android MediaPlayer 在 Android 4.2.2 上播放视频时闪烁的主要内容,如果未能解决你的问题,请参考以下文章

Android:MediaPlayer 未发布就完成了

在 Android 的 MediaPlayer 上播放音频时遇到问题

在 Android 中是不是可以录制 MediaPlayer 播放的内容?

Android -- MediaPlayer内部实现简析

Android MediaPlayer setDataSource failed

Android MediaPlayer Error/Info Code