欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

荐 android vlc集成以及了解vlc架构

程序员文章站 2022-03-07 20:13:13
一、近期整理了下之前用过的vlc for android;相关代码下载以及编译可以参考官方wiki下载地址,参考GitVLC源码结构对应功能./config/从命令行和配置文件中加载配置./control/提供动作控制功能,如播放等操作./extras/大多是平台的特殊代码./modules/模块管理./network/提供网络接口(socket管理,网络接口)./osd/显示屏幕上的操作./test/libvlc测试模块./...

一、近期整理了下之前用过的vlc for android;相关代码下载以及编译可以参考官方WIKI

官方Code下载地址,参考Git

个人Demo地址链接

https://github.com/xiaxiaxa/mgtv_vlc_demo
荐
                                                        android vlc集成以及了解vlc架构

二、搭建编译环境,编译vlc生成apk以及相对应的解码库等;

在ubuntu 14.04下面搭建编译环境,安装相关编译工具等;
下载代码:
git clone https://code.videolan.org/videolan/vlc-android.git
荐
                                                        android vlc集成以及了解vlc架构荐
                                                        android vlc集成以及了解vlc架构

需要配置NDK、JDK等环境变量等:
荐
                                                        android vlc集成以及了解vlc架构
ubuntu相关交叉编译工具等可以使用附件sh自动安装

链接: https://pan.baidu.com/s/10s1aoF_SHacT9rEsueJP_A
提取码: aja2

超级用户下,直接运行脚本会安装对应的arm交叉编译工具等;

对应的编译模块以及相关命令:

编译模块 命令 生成路径
编译apk sh compile.sh -a armeabi-v7a vlc-android/build/outputs/apk/vanillaARMv7/debug
编译aar sh compile.sh -l -a armeabi-v7a -r libvlc/build/outputs/aar
编译so sh compile-libvlc.sh -a armeabi-v7a libvlc/private_libs/libs/armeabi-v7a

荐
                                                        android vlc集成以及了解vlc架构
aar里面的是libvlc编译出来的相关so库
荐
                                                        android vlc集成以及了解vlc架构
个人编译的相关库以及vlc apk上传至网盘

提取码: h5hv

荐
                                                        android vlc集成以及了解vlc架构

三、附上个人的Git使用demo,测试rtp/rtsp等直播流是没问题的;(个人主要用vlc来解决直播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播放的控制主要是这两个类实现:**

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();
    }

荐
                                                        android vlc集成以及了解vlc架构
可以自己把jni部分源码复写,播放主要控制在libvlc

荐
                                                        android vlc集成以及了解vlc架构
荐
                                                        android vlc集成以及了解vlc架构

四、前期工作做完后,我们可以先来简要了解vlc的播放器工作;音视频的播放我们一般有几个步骤,主要是:

步骤 工作内容 简要说明
1 access访问 接收、获取、得到数据资源,包括解析访问源(url), 使用http协议,rtsp协议,ftp协议,建立连接,获取数据
2 demux解复杂 音频和视频分离,当然也有可能有字幕。通过分析数据包头来判断是什么数据文件,需要用什么解码格式
3 decode解码 包括音频和视频解码,或者软件解码和硬件解码。
4 output输出 分为音频和视频的输出(aout和vout)
VLC源码结构 对应功能
./config/ 从命令行和配置文件中加载配置
./control/ 提供动作控制功能,如播放等操作
./extras/ 大多是平台的特殊代码
./modules/ 模块管理
./network/ 提供网络接口(socket管理,网络接口)
./osd/ 显示屏幕上的操作
./test/ libvlc测试模块
./text/ 字符集
./interface/ 提供代码中可以调用的接口,如按键后的硬件作出反应
./playlist/ 管理播放功能
./input/ 建立并读取一个输入流,并且分离其中的音频和视频,然后把分离好的音频和视频流发给解码器
./audio_output/ 初始化音频混合器,即设置正确的同步频率,并对从解码器传来的音频流重新取样
./video_output/ 初始化视频播放器,把从解码器得到视频画面转化格式从yuv到rgb,然后播放
./stream_output/ / 输出音频流和视频流到网络
./misc/ libvlc使用的其他部分功能,如线程系统,消息队列等
  • VLC
    采用全模块化结构,通过动态的载入所需的模块,放入一个module_bank的结构体中统一管理,连VLC的Main模块也是通过插件的方式动态载入的(通过module_InitBank函数在初始化建立module_bank时)。对于不支持动态载入插件的系统环境中,VLC也可以采用builtin的方式,在VLC启动的时候静态载入所需要的插件,并放入module_bank统一管理。

五、附上自己编译的源码Vlc生成的apk以及demo生成的apk;

链接: https://pan.baidu.com/s/1YnPus6koPLI4-LEUzPADlA
提取码: w7ya

本文地址:https://blog.csdn.net/xiaweilihai/article/details/107232497