Page 1 of 1

Android LibVLC - Record streaming

Posted: 12 Oct 2018 19:48
by lucksg
Hello,

I am streaming RTSP link and I am using the following class that is working. I have taken the main idea of this class from an official git repository:

https://code.videolan.org/videolan/libv ... ivity.java

Now, I have to add a feature. Pressing a button I would be able to record the streaming while I am watching this and save the file to disk. How can I achieve this?

Code: Select all

import android.annotation.TargetApi; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.support.v7.widget.AppCompatButton; 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 android.widget.RelativeLayout; import android.widget.TextView; import org.videolan.libvlc.IVLCVout; import org.videolan.libvlc.LibVLC; import org.videolan.libvlc.Media; import org.videolan.libvlc.MediaPlayer; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Locale; public class VideoCameraActivity extends AppCompatActivity implements IVLCVout.OnNewVideoLayoutListener { static final String TAG = VideoCameraActivity.class.getSimpleName(); public static final String LINK_RTSP = "EXTRA_LINK_RTSP"; public static final String CAMERA_NAME = "EXTRA_CAMERA_NAME"; private String mLinkSource; private String mCameraName; 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 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 RelativeLayout mLoadingView; private RelativeLayout mRetryView; private AppCompatButton mBtnRetry; 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; private TextView mTitleText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_camera); Log.d(TAG,"onCreate"); mLinkSource = getIntent().getStringExtra(LINK_RTSP); mCameraName = getIntent().getStringExtra(CAMERA_NAME); final ArrayList<String> args = new ArrayList<>(); args.add("-vvv"); args.add("--vout=android-display"); 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 = (ViewStub) 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; } findAndInitViews(); setBackgroundImage(); mVideoSurfaceFrame.setVisibility(View.INVISIBLE); } @Override protected void onDestroy() { super.onDestroy(); mMediaPlayer.release(); mLibVLC.release(); } public void showLoading(boolean active) { if (active) mLoadingView.setVisibility(View.VISIBLE); else mLoadingView.setVisibility(View.GONE); } public void showRetry(boolean active) { if (active) mRetryView.setVisibility(View.VISIBLE); else mRetryView.setVisibility(View.GONE); } @Override protected void findAndInitViews() { mTitleText = findViewById(R.id.text_title); mLoadingView = findViewById(R.id.view_loading); mRetryView = findViewById(R.id.view_retry); mBtnRetry = findViewById(R.id.btn_retry); mTitleText.setText(mCameraName); mBtnRetry.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startMedia(); } }); } @Override protected void onStop() { super.onStop(); if (mOnLayoutChangeListener != null) { mVideoSurfaceFrame.removeOnLayoutChangeListener(mOnLayoutChangeListener); mOnLayoutChangeListener = null; } mMediaPlayer.stop(); mMediaPlayer.getVLCVout().detachViews(); } public void startMedia(){ showRetry(false); showLoading(true); Media media = new Media(mLibVLC, Uri.parse(mLinkSource)); mMediaPlayer.setMedia(media); media.release(); mMediaPlayer.setEventListener(new MediaPlayer.EventListener() { @Override public void onEvent(MediaPlayer.Event event) { if(event.type == MediaPlayer.Event.EncounteredError){ showLoading(false); showRetry(true); } } }); mMediaPlayer.play(); } @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); startMedia(); 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); } 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 >= 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 && 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) { mVideoWidth = width; mVideoHeight = height; mVideoVisibleWidth = visibleWidth; mVideoVisibleHeight = visibleHeight; mVideoSarNum = sarNum; mVideoSarDen = sarDen; updateVideoSurfaces(); showLoading(false); mVideoSurfaceFrame.setVisibility(View.VISIBLE); } }

Thanks to everyone.