代码之家  ›  专栏  ›  技术社区  ›  Johan Jarvi

如何在Android LibVLC中全屏显示RTSP流?

  •  1
  • Johan Jarvi  · 技术社区  · 6 年前

    我正在使用mrmaffen的VLC-ANDROID-SDK开发一个RTSP流媒体应用程序。 https://github.com/mrmaffen/vlc-android-sdk

    我已经成功地让它工作并运行得很好,但我无法摆脱的问题是让它全屏显示在SurfaceView上,甚至只是在SurfaceView的中心。

    这就是我得到的:

    http://s1378.photobucket.com/user/Jo_Han_Solo/media/Screenshot_20171214-125504_zps437k1kw2.png.html?filters[user]=146993343&filters[recent]=1&sort=1&o=1

    黑色窗口是屏幕的总大小,我想让视频填满屏幕,希望总是从中间填满,但我不知道怎么做。

    有没有人有过类似的经验,知道如何修复它?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Johan Jarvi    6 年前

    我有点解决了这个问题,但有点狡猾,这还远远不够完整,但考虑到缺乏关于这个主题的知识和信息,我认为这可能暂时对某人有所帮助。

    1. 查找屏幕的大小。
    2. 设置最终IVLCOut以合并屏幕大小。
    3. 将设置刻度调整为“全屏”视频流。

    解释每项任务:

    1. 设置全局设置:

      public class SingleStreamView extends AppCompatActivity implements 
      IVLCVout.Callback {
      
      public int mHeight;
      public int mWidth;
      

    其次,在onCreate任务中,找到设备的屏幕大小:

        DisplayMetrics displayMetrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
            mHeight = displayMetrics.heightPixels;
            mWidth = displayMetrics.widthPixels;
    

    2. 然后转到“CreatePlayer”活动,在那里设置视频输出:

        // Set up video output
            final IVLCVout vout = mMediaPlayer.getVLCVout();
            vout.setVideoView(mSurface);
            vout.setWindowSize(mWidth,mHeight);
            vout.addCallback(this);
            vout.attachViews();
    

    使它在我的曲面中居中的获胜线是“vout.setWindowSize(mWidth,mHeight);”

    然后,我简单地使用setscale选项“全屏”显示视频。也就是说,这有点 乱劈 这样做的方式,我想尝试并找出一种方法来获取编解码器信息,以便动态设置视频的比例,并通过这种方式自动将每个大小的视频流全屏显示到任何大小的屏幕,但目前这将适用于已知的视频流分辨率,它将自动调整到您手机的屏幕大小。

    不管怎样,我发现对于三星Galaxy s8,640x480p RTSP流的良好比例因子为1.8。编码如下:

            Media m = new Media(libvlc, Uri.parse(RTSP_ADDRESS));
            m.setHWDecoderEnabled(true,false);
            m.addOption(":network-caching=100");
            m.addOption(":clock-jitter=0");
            m.addOption(":clock-synchro=0");
            m.addOption(":fullscreen");
            mMediaPlayer.setMedia(m);
            mMediaPlayer.setAspectRatio("16:9");
            mMediaPlayer.setScale(1.8f);
            mMediaPlayer.play();
    

    这里有“mmediplayer.setScale(1.8f);”

    希望这对别人有帮助!

        2
  •  1
  •   iOS-Coder    6 年前

    你的解决方案似乎很有趣,但我面临着同样的问题,我似乎无法用你的方法解决这些问题。

    我目前获得的屏幕截图可以在以下位置看到: https://photos.app.goo.gl/9nKo22Mkc2SZq4SK9

    我还想(垂直)在三星XCover4(720x1280像素)和最小分辨率为320x480的设备上以横向/纵向模式将rtsp视频流居中。我希望运行的最低Android SDK版本是API-22(Android 5.1.1)。 我让(嵌入式)VLC播放器工作的libvlc代码基于de.mrmaffen:libvlc-android:2.1。12@aar'.

    鉴于上述“要求”,您可以在屏幕截图中看到以下行为。前两个屏幕截图位于Samsung-XCover4(720x1280)上,您可以看到设备方向=横向剪辑视频,并且不会对其进行缩放,而第三个和第四个屏幕截图显示,在分辨率较低的设备上,相同的视频流不会遵循SURFACE\u BEST\u FIT方法(请参阅下面的代码了解解释)。 我希望看到updateVideoSurfaces来处理设备方向的变化,或者至少在启动时显示整个视频。

    我的VLC视频播放器(垂直线性布局的一部分)的布局如下:

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.3"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:orientation="vertical">
    
        <FrameLayout
            android:id="@+id/video_surface_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:foregroundGravity="clip_horizontal|clip_vertical"
            tools:ignore="true">
    
            <ViewStub
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout="@layout/surface_view"
                android:id="@+id/surface_stub" />
    
            <ViewStub
                android:layout_width="1dp"
                android:layout_height="1dp"
                android:layout="@layout/surface_view"
                android:id="@+id/subtitles_surface_stub" />
    
            <ViewStub
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout="@layout/texture_view"
                android:id="@+id/texture_stub" />
    
        </FrameLayout>
    
    </LinearLayout>
    

    我从de.mrmaffen获得的示例代码使用了updateVideoSurfaces(见下面的java代码),它使用了许多SURFACE\u XX方法,在我看来,这些方法似乎涵盖了具有不同设备方向和分辨率的所有场景。

    出于某种原因,我无法理解为什么这样做不起作用,我怀疑我正在为播放器使用的布局(FrameLayout/ViewStub)可能会导致这些问题。

    我想知道您是否可以说明方向,以确保视频流在任何设备方向/分辨率上自动缩放/居中。

    我使用的播放器代码如下:

    package com.testing.vlc2player;
    
    import ...
    
    public class VLC2PlayerActivity extends AppCompatActivity implements IVLCVout.OnNewVideoLayoutListener,
            IVLCVout.Callback {
    
        private static final Logger log = LoggerFactory.getLogger(VLC2PlayerActivity.class);
    
        private static final boolean USE_SURFACE_VIEW = true;
        private static final boolean ENABLE_SUBTITLES = false;
        private static final int SURFACE_BEST_FIT = 0;
        private static final int SURFACE_FIT_SCREEN = 1;
        private static final int SURFACE_FILL = 2;
        private static final int SURFACE_16_9 = 3;
        private static final int SURFACE_4_3 = 4;
        private static final int SURFACE_ORIGINAL = 5;
        private static final int CURRENT_SIZE = SURFACE_BEST_FIT;
    
        private FrameLayout mVideoSurfaceFrame = null;
        private SurfaceView mVideoSurface = null;
        private SurfaceView mSubtitlesSurface = null;
        private TextureView mVideoTexture = null;
        private View mVideoView = null;
    
        private final Handler mHandler = new Handler();
        private View.OnLayoutChangeListener mOnLayoutChangeListener = null;
    
        private LibVLC mLibVLC = null;
        private MediaPlayer mMediaPlayer = null;
        private int mVideoHeight = 0;
        private int mVideoWidth = 0;
        private int mVideoVisibleHeight = 0;
        private int mVideoVisibleWidth = 0;
        private int mVideoSarNum = 0;
        private int mVideoSarDen = 0;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_video_player);
    
            setupVLCLayout();
        }
    
        private void setupVLCLayout() {
            log.debug("...");
            final ArrayList<String> args = new ArrayList<>();
            args.add("-vvv");
            mLibVLC = new LibVLC(this, args);
            mMediaPlayer = new MediaPlayer(mLibVLC);
    
            mVideoSurfaceFrame = findViewById(R.id.video_surface_frame);
            if (USE_SURFACE_VIEW) {
                ViewStub stub = findViewById(R.id.surface_stub);
                mVideoSurface = (SurfaceView) stub.inflate();
                if (ENABLE_SUBTITLES) {
                    stub = findViewById(R.id.subtitles_surface_stub);
                    mSubtitlesSurface = (SurfaceView) stub.inflate();
                    mSubtitlesSurface.setZOrderMediaOverlay(true);
                    mSubtitlesSurface.getHolder().setFormat(PixelFormat.TRANSLUCENT);
                }
                mVideoView = mVideoSurface;
            } else {
                ViewStub stub = findViewById(R.id.texture_stub);
                mVideoTexture = (TextureView) stub.inflate();
                mVideoView = mVideoTexture;
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mMediaPlayer.release();
            mLibVLC.release();
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            final IVLCVout vlcVout = mMediaPlayer.getVLCVout();
            if (mVideoSurface != null) {
                vlcVout.setVideoView(mVideoSurface);
                if (mSubtitlesSurface != null) {
                    vlcVout.setSubtitlesView(mSubtitlesSurface);
                }
            } else {
                vlcVout.setVideoView(mVideoTexture);
            }
            vlcVout.attachViews(this);
    
            String url = getString(R.string.videoURL);
            Uri uri = Uri.parse(url);
            final Media media = new Media(mLibVLC, uri);
            mMediaPlayer.setMedia(media);
            media.release();
            mMediaPlayer.play();
    
            if (mOnLayoutChangeListener == null) {
                mOnLayoutChangeListener = new View.OnLayoutChangeListener() {
                    private final Runnable mRunnable = new Runnable() {
                        @Override
                        public void run() {
                            updateVideoSurfaces();
                        }
                    };
                    @Override
                    public void onLayoutChange(View v, int left, int top, int right,
                                               int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                        if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
                            mHandler.removeCallbacks(mRunnable);
                            mHandler.post(mRunnable);
                        }
                    }
                };
            }
            mVideoSurfaceFrame.addOnLayoutChangeListener(mOnLayoutChangeListener);
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            if (mOnLayoutChangeListener != null) {
                mVideoSurfaceFrame.removeOnLayoutChangeListener(mOnLayoutChangeListener);
                mOnLayoutChangeListener = null;
            }
            mMediaPlayer.stop();
            mMediaPlayer.getVLCVout().detachViews();
        }
    
        private void changeMediaPlayerLayout(int displayW, int displayH) {
            log.debug("displayW={}, displayH={}", displayW, displayH);
            /* Change the video placement using the MediaPlayer API */
            int dispWd = displayW;
            int dispHt = displayH;
            dispWd = mVideoSurface.getWidth();  //Note: we do NOT want to use the entire display!
            dispHt = mVideoSurface.getHeight();
            switch (CURRENT_SIZE) {
                case SURFACE_BEST_FIT:
                    mMediaPlayer.setAspectRatio(null);
                    mMediaPlayer.setScale(0);
                    break;
                case SURFACE_FIT_SCREEN:
                case SURFACE_FILL: {
                    Media.VideoTrack vtrack = mMediaPlayer.getCurrentVideoTrack();
                    if (vtrack == null) {
                        return;
                    }
                    final boolean videoSwapped = vtrack.orientation == Media.VideoTrack.Orientation.LeftBottom
                            || vtrack.orientation == Media.VideoTrack.Orientation.RightTop;
                    if (CURRENT_SIZE == SURFACE_FIT_SCREEN) {
                        int videoW = vtrack.width;
                        int videoH = vtrack.height;
                        if (videoSwapped) {
                            int swap = videoW;
                            videoW = videoH;
                            videoH = swap;
                        }
                        if (vtrack.sarNum != vtrack.sarDen) {
                            videoW = videoW * vtrack.sarNum / vtrack.sarDen;
                        }
                        float ar = videoW / (float) videoH;
                        float dar = dispWd / (float) dispHt;
                        //noinspection unused
                        float scale;
                        if (dar >= ar) {
                            scale = dispWd / (float) videoW; /* horizontal */
                        } else {
                            scale = dispHt / (float) videoH; /* vertical */
                        }
                        log.debug("scale={}", scale);
                        mMediaPlayer.setScale(scale);
                        mMediaPlayer.setAspectRatio(null);
                    } else {
                        mMediaPlayer.setScale(0);
                        mMediaPlayer.setAspectRatio(!videoSwapped ? ""+dispWd+":"+dispHt
                                : ""+dispHt+":"+dispWd);
                    }
                    break;
                }
                case SURFACE_16_9:
                    mMediaPlayer.setAspectRatio("16:9");
                    mMediaPlayer.setScale(0);
                    break;
                case SURFACE_4_3:
                    mMediaPlayer.setAspectRatio("4:3");
                    mMediaPlayer.setScale(0);
                    break;
                case SURFACE_ORIGINAL:
                    mMediaPlayer.setAspectRatio(null);
                    mMediaPlayer.setScale(1);
                    break;
            }
        }
    
        private void updateVideoSurfaces() {
            log.debug("...");
            int sw = getWindow().getDecorView().getWidth();
            int sh = getWindow().getDecorView().getHeight();
            // sanity check
            if (sw * sh == 0) {
                log.error("Invalid surface size");
                return;
            }
    
            mMediaPlayer.getVLCVout().setWindowSize(sw, sh);
    
            ViewGroup.LayoutParams lp = mVideoView.getLayoutParams();
            if (mVideoWidth * mVideoHeight == 0) {
                /* Case of OpenGL vouts: handles the placement of the video using MediaPlayer API */
                lp.width  = ViewGroup.LayoutParams.MATCH_PARENT;
                lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
                mVideoView.setLayoutParams(lp);
                lp = mVideoSurfaceFrame.getLayoutParams();
                lp.width  = ViewGroup.LayoutParams.MATCH_PARENT;
                lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
                mVideoSurfaceFrame.setLayoutParams(lp);
                changeMediaPlayerLayout(sw, sh);
                return;
            }
    
            if (lp.width == lp.height && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                /* We handle the placement of the video using Android View LayoutParams */
                mMediaPlayer.setAspectRatio(null);
                mMediaPlayer.setScale(0);
            }
    
            double dw = sw, dh = sh;
            final boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
    
            if (sw > sh && isPortrait || sw < sh && !isPortrait) {
                dw = sh;
                dh = sw;
            }
    
            // compute the aspect ratio
            double ar, vw;
            if (mVideoSarDen == mVideoSarNum) {
                /* No indication about the density, assuming 1:1 */
                vw = mVideoVisibleWidth;
                ar = (double)mVideoVisibleWidth / (double)mVideoVisibleHeight;
            } else {
                /* Use the specified aspect ratio */
                vw = mVideoVisibleWidth * (double)mVideoSarNum / mVideoSarDen;
                ar = vw / mVideoVisibleHeight;
            }
    
            // compute the display aspect ratio
            double dar = dw / dh;
    
            switch (CURRENT_SIZE) {
                case SURFACE_BEST_FIT:
                    if (dar < ar) {
                        dh = dw / ar;
                    } else {
                        dw = dh * ar;
                    }
                    break;
                case SURFACE_FIT_SCREEN:
                    if (dar >= ar) {
                        dh = dw / ar; /* horizontal */
                    } else {
                        dw = dh * ar; /* vertical */
                    }
                    break;
                case SURFACE_FILL:
                    break;
                case SURFACE_16_9:
                    ar = 16.0 / 9.0;
                    if (dar < ar) {
                        dh = dw / ar;
                    } else {
                        dw = dh * ar;
                    }
                    break;
                case SURFACE_4_3:
                    ar = 4.0 / 3.0;
                    if (dar < ar) {
                        dh = dw / ar;
                    } else {
                        dw = dh * ar;
                    }
                    break;
                case SURFACE_ORIGINAL:
                    dh = mVideoVisibleHeight;
                    dw = vw;
                    break;
            }
    
            // set display size
            lp.width  = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
            lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
            mVideoView.setLayoutParams(lp);
            if (mSubtitlesSurface != null) {
                mSubtitlesSurface.setLayoutParams(lp);
            }
            // set frame size (crop if necessary)
            lp = mVideoSurfaceFrame.getLayoutParams();
            lp.width = (int) Math.floor(dw);
            lp.height = (int) Math.floor(dh);
            mVideoSurfaceFrame.setLayoutParams(lp);
    
            mVideoView.invalidate();
            if (mSubtitlesSurface != null) {
                mSubtitlesSurface.invalidate();
            }
        }
    
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public void onNewVideoLayout(IVLCVout vlcVout, int width, int height,
                                     int visibleWidth, int visibleHeight,
                                     int sarNum, int sarDen) {
            log.debug("...");
            mVideoWidth = width;
            mVideoHeight = height;
            mVideoVisibleWidth = visibleWidth;
            mVideoVisibleHeight = visibleHeight;
            mVideoSarNum = sarNum;
            mVideoSarDen = sarDen;
            updateVideoSurfaces();
        }
    
        @Override
        public void onSurfacesCreated(IVLCVout vlcVout) {
            log.debug("vlcVout={}", vlcVout);
        }
    
        /**
         * This callback is called when surfaces are destroyed.
         */
        public void onSurfacesDestroyed(IVLCVout vlcVout) {
            log.debug("vlcVout={}", vlcVout);
        }
    
        public void onStopClientMonitoring(View view) {
    //        log.info("UI -> Stop monitoring clientId= ...");
    //        onBackPressed();
            String androidSDKRelease = Build.VERSION.RELEASE;
            int androidSDKInt = Build.VERSION.SDK_INT;
            String androidInfo = String.format(Locale.getDefault(), "Android %s (Version %d)", androidSDKRelease, androidSDKInt);
            String appVersionName = BuildConfig.VERSION_NAME;
            String appName = getString(R.string.app_name);
            String appInfoTitle = String.format(getString(R.string.app_info_title), appName);
            String infoMsg = String.format(getString(R.string.app_info_message), appVersionName, androidInfo);
    
            new AlertDialog.Builder(this).setTitle(appInfoTitle)
                    .setMessage(infoMsg)
                    .setPositiveButton(getString(R.string.button_ok), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // Dismiss dialog
                            dialog.dismiss();
                        }
                    })
                    .create()
                    .show();
        }
    }