title: LibVLC for Android - Stretch Live Stream RTSP to desired aspect ratio on SurfaceView
body: <p>Okay so I'm using LibVLC for Android (with Android Studio) to receive the live RTSP stream of an IP Camera since VideoView doesn't quite support LIVE streams. I'm using a sample code from the VideoLAN people which can be found here:</p>
  
  <p><a href="https://code.videolan.org/videolan/libvlc-android-samples" rel="nofollow noreferrer">https://code.videolan.org/videolan/libvlc-android-samples</a></p>
  
  <p>And I've done a lot of investigation on the code to achieve 19:6 aspect ratio out of the 4:3 that my camera outputs. The reason why I'm trying to break the aspect ratio is because this IP Camera records 1280x720 pixels but outputs 640x480 through it's second stream. The problem is that the width isn't cropped, but stretched from the sides, so it looks kinda compressed tight.</p>
  
  <p>I've tried setting the 4 alignParent options to true on the SurfaceView, but no results. Also tried to multiply some of the width variables I found there in the JavaActivity class code by 1.33333 which should theorically stretch the width, but nothing happened at all, not even an error or an exception. I also tried making a new class extending SurfaceView and tweaking with the onMeasure method, but no dice. This is the JavaActivity code as is from the example, (of course I've adapted mine to work with my project but with minor changes)</p>
  
  <pre><code>package org.videolan.javasample;
  
  import android.annotation.TargetApi;
  import android.content.res.Configuration;
  import android.graphics.PixelFormat;
  import android.net.Uri;
  import android.os.Build;
  import android.os.Handler;
  import android.support.v7.app.AppCompatActivity;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.SurfaceView;
  import android.view.TextureView;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.ViewStub;
  import android.widget.FrameLayout;
  
  import org.videolan.libvlc.IVLCVout;
  import org.videolan.libvlc.LibVLC;
  import org.videolan.libvlc.Media;
  import org.videolan.libvlc.MediaPlayer;
  
  import java.util.ArrayList;
  
  public class JavaActivity extends AppCompatActivity implements IVLCVout.OnNewVideoLayoutListener {
      private static final boolean USE_SURFACE_VIEW = true;
      private static final boolean ENABLE_SUBTITLES = true;
      private static final String TAG = "JavaActivity";
      private static final String SAMPLE_URL = "http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_640x360.m4v"; 
  // Not the actual RTSP Live Stream link but you know...
          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 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_main);
  
          final ArrayList&lt;String&gt; args = new ArrayList&lt;&gt;();
          args.add("-vvv");
          mLibVLC = new LibVLC(this, args);
          mMediaPlayer = new MediaPlayer(mLibVLC);
  
          mVideoSurfaceFrame = (FrameLayout) findViewById(R.id.video_surface_frame);
          if (USE_SURFACE_VIEW) {
              ViewStub stub = (ViewStub) findViewById(R.id.surface_stub);
              mVideoSurface = (SurfaceView) stub.inflate();
              if (ENABLE_SUBTITLES) {
                  stub = (ViewStub) findViewById(R.id.subtitles_surface_stub);
                  mSubtitlesSurface = (SurfaceView) stub.inflate();
                  mSubtitlesSurface.setZOrderMediaOverlay(true);
                  mSubtitlesSurface.getHolder().setFormat(PixelFormat.TRANSLUCENT);
              }
              mVideoView = mVideoSurface;
          }
          else
          {
              ViewStub stub = (ViewStub) 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);
  
          Media media = new Media(mLibVLC, Uri.parse(SAMPLE_URL));
          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) {
          /* Change the video placement using the MediaPlayer API */
          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 = displayW / (float) displayH;
  
                      float scale;
                      if (dar &gt;= ar)
                          scale = displayW / (float) videoW; /* horizontal */
                      else
                          scale = displayH / (float) videoH; /* vertical */
                      mMediaPlayer.setScale(scale);
                      mMediaPlayer.setAspectRatio(null);
                  } else {
                      mMediaPlayer.setScale(0);
                      mMediaPlayer.setAspectRatio(!videoSwapped ? ""+displayW+":"+displayH
                              : ""+displayH+":"+displayW);
                  }
                  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() {
          int sw = getWindow().getDecorView().getWidth();
          int sh = getWindow().getDecorView().getHeight();
  
          // sanity check
          if (sw * sh == 0) {
              Log.e(TAG, "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 &amp;&amp; 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 &gt; sh &amp;&amp; isPortrait || sw &lt; sh &amp;&amp; !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 &lt; ar)
                      dh = dw / ar;
                  else
                      dw = dh * ar;
                  break;
              case SURFACE_FIT_SCREEN:
                  if (dar &gt;= 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 &lt; ar)
                      dh = dw / ar;
                  else
                      dw = dh * ar;
                  break;
              case SURFACE_4_3:
                  ar = 4.0 / 3.0;
                  if (dar &lt; 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) {
          mVideoWidth = width;
          mVideoHeight = height;
          mVideoVisibleWidth = visibleWidth;
          mVideoVisibleHeight = visibleHeight;
          mVideoSarNum = sarNum;
          mVideoSarDen = sarDen;
          updateVideoSurfaces();
      }
      }
  </code></pre>
  
  <p>This is how it's looking right now:
  <a href="https://i.stack.imgur.com/8adLY.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/8adLY.jpg" alt="Without corrective stretching"></a></p>
  
  <p>And this is how I'd like it to show:
  <a href="https://i.stack.imgur.com/JcyJ1.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/JcyJ1.jpg" alt="With corrective stretching"></a></p>
  
  <p>I've photoshopped the second one btw.</p>
  
  <p>Any help is appreciated. If you need any more data, just let me know.</p>