1. I have recently sorted out the vlc for android that I used before; the relevant code download and compilation can refer to the official website WIKI
Download address, reference Git
personal Demo address
https://github.com/xiaxiaxa/mgtv_vlc_demo:
2. Build a compilation environment, compile vlc to generate apk and the corresponding decoding library, etc.;
Build a compilation environment under ubuntu 14.04, install related compilation tools, etc.;
Download code:
git clone https://code.videolan.org/videolan/vlc-android.git
You need to configure environment variables such as NDK, JDK, etc.:
ubuntu related cross-compilation tools, etc. can be automatically installed using the attachment sh
Link: https://pan.baidu.com/s/10s1aoF_SHacT9rEsueJP_A
Extraction code: aja2
Under the super user, directly running the script will install the corresponding arm cross-compilation tools, etc.;
The corresponding compilation module and related commands:
compile module | Order | generate path |
---|---|---|
compile apk | sh compile.sh -a armeabi-v7a | vlc-android/build/outputs/apk/vanillaARMv7/debug |
compile aar | sh compile.sh -l -a armeabi-v7a -r | libvlc/build/outputs/aar |
compile so | sh compile-libvlc.sh -a armeabi-v7a | libvlc/private_libs/libs/armeabi-v7a |
Inside aar is the related so library compiled by libvlc
Personally compiled related libraries and vlc apk uploaded to netdisc
Extraction code: h5hv
3. Attach personal Git usage demo , it is no problem to test live streams such as rtp/rtsp; (personally, I mainly use vlc to solve playback problems such as live rtsp)
public class VlcPlayActivity extends AppCompatActivity implements IVLCVout.OnNewVideoLayoutListener { private static final boolean ENABLE_SUBTITLES = true; private static final String TAG = "VlcActivity"; public static String SAMPLE_URL = "rtp://239.76.245.115:1234"; 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 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.vlc_play); SAMPLE_URL = getIntent().getStringExtra("extra_url"); final ArrayList<String> args = new ArrayList<>(); args.add("-vvv"); mLibVLC = new LibVLC(this, args); mMediaPlayer = new MediaPlayer(mLibVLC); mVideoSurfaceFrame = (FrameLayout) findViewById(R.id.video_surface_frame); mVideoSurface = (SurfaceView) findViewById(R.id.video_surface); if (ENABLE_SUBTITLES) { final ViewStub stub = (ViewStub) findViewById(R.id.subtitles_stub); mSubtitlesSurface = (SurfaceView) stub.inflate(); mSubtitlesSurface.setZOrderMediaOverlay(true); mSubtitlesSurface.getHolder().setFormat(PixelFormat.TRANSLUCENT); } } @Override protected void onPause() { super.onPause(); releaseData(); } @Override protected void onDestroy() { super.onDestroy(); releaseData(); } private void releaseData() { if (isFinishing()) { if (mOnLayoutChangeListener != null) { if (mVideoSurfaceFrame != null) { mVideoSurfaceFrame.removeOnLayoutChangeListener(mOnLayoutChangeListener); } mOnLayoutChangeListener = null; } if (mMediaPlayer != null) { mMediaPlayer.getVLCVout().detachViews(); mMediaPlayer.pause(); mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; } if (mSubtitlesSurface != null) { mSubtitlesSurface = null; } if (mHandler != null) { mHandler.removeCallbacks(null); } } } @Override protected void onStart() { super.onStart(); final IVLCVout vlcVout = mMediaPlayer.getVLCVout(); vlcVout.setVideoView(mVideoSurface); if (mSubtitlesSurface != null) vlcVout.setSubtitlesView(mSubtitlesSurface); vlcVout.attachViews(this); Media media = new Media(mLibVLC, Uri.parse("rtp://239.76.245.115:1234")); 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(); releaseData(); } 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; } if (mMediaPlayer != null) { mMediaPlayer.getVLCVout().setWindowSize(sw, sh); } if (mVideoSurface != null) { ViewGroup.LayoutParams lp = mVideoSurface.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; mVideoSurface.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 */ if (mMediaPlayer != null) { 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); mVideoSurface.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); mVideoSurface.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; if (isFinishing()){ } else { updateVideoSurfaces(); } } } **demo The control of playback is mainly implemented by these two classes:** public class PlayerActivity extends BaseActivity { private static final String TAG = "PlayerActivity"; @ViewInject(R.id.video_play) private VideoView mVideoPlay; @ViewInject(R.id.progress_loading) private ProgressBar mProgresLoading; private String mUrl; @Override public int getLayoutRes() { return R.layout.activity_video_player; } @Override public void init() { mUrl = getIntent().getStringExtra(ConstData.IntentKey.VIDEO_URL); if(TextUtils.isEmpty(mUrl)) finish(); mVideoPlay.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.i(TAG, "onError"); mProgresLoading.setVisibility(View.GONE); finish(); return true; } }); mVideoPlay.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { Log.i(TAG, "onPrepared"); mProgresLoading.setVisibility(View.GONE); } }); } @Override protected void onResume() { super.onResume(); playVideo(); } @Override protected void onPause() { super.onPause(); mVideoPlay.pause(); } @Override protected void onStop() { super.onStop(); try{ mVideoPlay.stopPlayback(); }catch (Exception e){ } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK){ mProgresLoading.setVisibility(View.GONE); finish(); return true; } return super.onKeyDown(keyCode, event); } private void playVideo(){ try{ mVideoPlay.stopPlayback(); }catch (Exception e){ } mProgresLoading.setVisibility(View.VISIBLE); mVideoPlay.setVideoURI(Uri.parse(mUrl)); mVideoPlay.start(); }
You can copy some of the jni source code yourself, and the playback is mainly controlled in libvlc
4. After the preliminary work is completed, we can first briefly understand the work of the vlc player; we generally have several steps for audio and video playback, mainly:
step | work content | brief introduction |
---|---|---|
1 | access access | Receive, obtain, and obtain data resources, including parsing the access source (url), using http protocol, rtsp protocol, ftp protocol, establishing connection, and obtaining data |
2 | demux solution complex | Audio and video are separated, and of course there may be subtitles. By analyzing the data packet header to determine what data file is and what decoding format needs to be used |
3 | decode decode | Including audio and video decoding, or software decoding and hardware decoding. |
4 | output output | Split into audio and video outputs (aout and vout) |
VLC source code structure | Corresponding function |
---|---|
./config/ | Load configuration from command line and configuration files |
./control/ | Provide motion control functions, such as playback and other operations |
./extras/ | Mostly platform-specific code |
./modules/ | Module management |
./network/ | Provide network interface (socket management, network interface) |
./osd/ | Display operations on the screen |
./test/ | libvlc test module |
./text/ | character set |
./interface/ | Provide an interface that can be called in the code, such as the hardware response after the key is pressed |
./playlist/ | Manage playback functions |
./input/ | Create and read an input stream, separate the audio and video from it, and send the separated audio and video streams to the decoder |
./audio_output/ | Initialize the audio mixer, i.e. set the correct sync frequency and resample the audio stream from the decoder |
./video_output/ | Initialize the video player, convert the video image obtained from the decoder from yuv to rgb, and then play |
./stream_output/ / | Output audio stream and video stream to network |
./misc/ | Other parts of the functions used by libvlc, such as threading system, message queue, etc. |
- VLC
The fully modular structure is adopted, and the required modules are dynamically loaded and put into a module_bank structure for unified management. Even the Main module of VLC is dynamically loaded through the plug-in (through the module_InitBank function when the module_bank is initialized and established) ). For system environments that do not support dynamic loading of plug-ins, VLC can also use
The builtin method statically loads the required plug-ins when VLC starts, and puts them into the module_bank for unified management.
5. Attach the apk generated by the source code Vlc compiled by yourself and the apk generated by the demo;
Link: https://pan.baidu.com/s/1YnPus6koPLI4-LEUzPADlA
Extraction code: w7ya