美文网首页多媒体科技开源库
深入理解VLC之纵观全局

深入理解VLC之纵观全局

作者: zhanghuicuc | 来源:发表于2019-07-15 22:17 被阅读18次

VLC,著名的开源播放器项目,它虽然很庞大,但是在架构设计上也高度模块化。幸运的是,官方wiki非常详细,无论是大的架构设计,还是每一个模块里面的代码细节,都有详尽的介绍。wiki链接:https://wiki.videolan.org/Hacker_Guide/

本文主要以vlc-android项目为例,介绍vlc的架构设计,参考了一篇对vlc架构分析得很好的文章:https://jiya.io/archives/vlc_learn_2.html

1. 代码结构

下载vlc-android项目代码并编译之后,得到主要目录结构如下

libvlc jni层以及java api
medialibrary 媒体数据库相关的代码
vlc native代码,后面分析的重点
vlc-android 应用的代码,编出来的apk就在vlc-andoird/debug目录下

在介绍vlc目录下的代码结构之前,先来说一下vlc的架构设计:

vlc的架构设计可以分作两层:

第一层是对整条播放通路(pipeline)的抽象,包含以下几个抽象概念

playlist: playlist表示播放列表,VLC在启动后,即创建一个playlist thread,用户输入后,动态创建input。
input: input表示输入,当用户通过界面输入一个文件或者流地址时,input thread 被动态创建,该线程的生命周期直到本次播放结束。
access: access表示访问,是VLC抽象的一个层,该层向下直接使用文件或网络IO接口,向上为stream层服务,提供IO接口。
stream: stream表示流,是VLC抽象的一个层,该层向下直接使用access层提供的IO接口,向上为demux层服务,提供IO接口。
demux: demux表示解复用,该层向下直接使用stream层提供的IO接口,数据出来后送es_out。
es_out: es_out表示输出,是VLC抽象的一个层,该层获取demux后的数据,送decode解码。
decode: decode表示解码,是视频技术中的概念,获取es_out出来的数据(通过一个fifo交互),解码后送output。
output: output表示输出,获取从decode出来的数据,送readerer。
readerer: readerer表示显示,获取从output出来的数据(通过一个fifo交互),然后显示。

第二层是对播放通路中每一个环节,都提供了诸多可选的module,例如decode,就同时提供了包含ffmpeg软解以及android mediacodec硬解在内的多个可选解码module。

在播放流程开始后,就按照平台、媒体格式选择合适的module,当然,我们也可以设置参数强制要求使用某些module。

有了以上概念之后,再来看vlc目录下的主要代码结构,如下(参考https://wiki.videolan.org/VLC_source_tree/

.
├──contrib 依赖库相关,所有依赖库列表参见https://wiki.videolan.org/Contrib_Status
│ ├── arm-linux-androideabi build之后的依赖库
│ ├── contrib-android-arm-linux-androideabi 依赖库代码
│ ├── src 依赖库的patch和makefile
│ └── tarballs 下载的依赖库tar包
├── lib libvlc的api
├── modules 各个模块的代码
│ ├── access
│ ├── audio_filter audio resample与格式转换
│ ├── audio_output audio输出,包含android audiotrack与opensl等
│ ├── codec
│ ├── demux
│ ├── text_renderer 文本绘制
│ ├── video_chroma 视频颜色格式转换
│ ├── video_filter 视频filter,如deinterlace,还包含一些滤镜,如老电影效果
│ ├── video_output 视频输出,包含android nativewindow,opengl等
├── src
├── android Android平台相关代码
├── audio_output 初始化audio mixer,从decoder拿到audio frames处理后输出
├── config 命令行参数解析
├── input
├── interface UI相关
├── linux linux 平台相关代码
├── misc 杂项,包含消息队列,picture fifo, mime type, cpu检测等功能的代码
├── modules module的选择与加载
├── network 网络通信相关
├── playlist 播放列表相关
├── stream_output vlc不仅是一个播放器,还能做推流,这里是推流相关代码
├── text 文本相关,如字符编码等
├── video_output 从decoder拿到视频帧和字幕,处理后输出

2. Java API使用

vlc也提供了一套MediaPlayer的API,但是没有按照原生Android的MediaPlayer接口进行封装,所以用起来会比较别扭,需要重新封装一下。使用方法可以参考https://code.videolan.org/videolan/libvlc-android-samples.git

import org.videolan.libvlc.IVLCVout;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.libvlc.MediaPlayer;
 
 
mLibVLC = new LibVLC(this, args);//设置启动参数
mMediaPlayer = new MediaPlayer(mLibVLC);
final IVLCVout vlcVout = mMediaPlayer.getVLCVout();
vlcVout.setVideoView(mVideoSurface);//设置视频显示surface
vlcVout.setSubtitlesView(mSubtitlesSurface);//设置字幕显示surface
vlcVout.attachViews(this);//设置IVLCVout.OnNewVideoLayoutListener的回调,注意:想要使用ANativeWindow的话,这里必须注册回调
final Media media = new Media(mLibVLC, getAssets().openFd(ASSET_FILENAME));
mMediaPlayer.setMedia(media);//相当于setdatasource
media.release();
mMediaPlayer.play();
 
 
//停止播放
mMediaPlayer.stop();
mMediaPlayer.getVLCVout().detachViews();
mMediaPlayer.release();
mLibVLC.release();
 
 
//设置显示窗口的size,宽高比,scale
mMediaPlayer.getVLCVout().setWindowSize(sw, sh);
mMediaPlayer.setAspectRatio("16:9");
mMediaPlayer.setScale(0);

3. 线程模型

在前面提到的参考文献中有一张很经典的图,如下

在这里插入图片描述

vlc中的线程都是通过vlc_clone_attr方法来创建,我们可以给这个方法加上一个线程名参数,方便调试,如下

src/android/thread.c
static int vlc_clone_attr (vlc_thread_t *th, void *(*entry) (void *),
                           void *data, bool detach, const char* threadName /*线程名*/)
{
    vlc_thread_t thread = malloc (sizeof (*thread));
   ...
 
    pthread_attr_t attr;
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate (&attr, detach ? PTHREAD_CREATE_DETACHED
                                               : PTHREAD_CREATE_JOINABLE);
 
    ret = pthread_create (&thread->thread, &attr,
                          detach ? detached_thread : joinable_thread, thread);
    pthread_attr_destroy (&attr);
    pthread_setname_np(thread->thread, threadName); //设置线程名
 
    pthread_sigmask (SIG_SETMASK, &oldset, NULL);
    *th = thread;
    return ret;
}

我自己加了一些线程名之后,播放一个流,用adb shell debuggerd -b 看一下都有哪些线程,如下

vlc-input: 即input thread
vlc-fetcher: 即playlist的FetcherThread
vlc-videooutput: 即src/video_output/video_output.c中的

static void *Thread(void *object)
{
    vout_thread_t *vout = object;
    vout_thread_sys_t *sys = vout->p;
 
 
    msg_Dbg(vout, "video output tid is %u", syscall(SYS_gettid));
    mtime_t deadline = VLC_TS_INVALID;
    bool wait = false;
    for (;;) {
        vout_control_cmd_t cmd;
 
        if (wait)
        {
            const mtime_t max_deadline = mdate() + 100000;
            deadline = deadline <= VLC_TS_INVALID ? max_deadline : __MIN(deadline, max_deadline);
        } else {
            deadline = VLC_TS_INVALID;
        }
        while (!vout_control_Pop(&sys->control, &cmd, deadline))
            if (ThreadControl(vout, cmd))
                return NULL;
 
        deadline = VLC_TS_INVALID;
        wait = ThreadDisplayPicture(vout, &deadline) != VLC_SUCCESS;
 
        const bool picture_interlaced = sys->displayed.is_interlaced;
 
        vout_SetInterlacingState(vout, picture_interlaced);
        vout_ManageWrapper(vout);
    }
}

NDK MediaCodec_:即NDK MediaCodec的线程
CodecLooper: 即stagefright中MediaCodec的线程
vlc-decoder: 即视频对应的DecoderThread,在src/input/decoder.c中
android_audiotrack:即音频对应的DecoderThread,其实也是个vlc-decoder,不过在modules/audio_output/audiotrack.c中调用android_getEnv,即AttachCurrentThread后线程名变了而已
AudioTrack:即framework的audiotrack线程
vlc-audiotrack:即音频输出线程,对应modules/audio_output/audiotrack.c中的AudioTrack_Thread

可以看到,和上面图片所描述的一致。


关注公众号,掌握更多安卓、多媒体领域知识与资讯,开启精彩程序人生

在这里插入图片描述

文章帮到你了?可以扫描如下二维码进行打赏,打赏多少您随意~

在这里插入图片描述

相关文章

  • 深入理解VLC之纵观全局

    VLC,著名的开源播放器项目,它虽然很庞大,但是在架构设计上也高度模块化。幸运的是,官方wiki非常详细,无论是大...

  • 深入理解VLC之代码流程

    1. 初始化流程 2. 创建input thread,解析输入url 值得注意的是,在input_EsOutNew...

  • 纵观全局 投资自己

    1.大处着眼 小处着手 看到这个就想到小时候班级又两种人,一种是每天认真听讲,埋头苦读最后成绩还是平平的学生,一种...

  • 深入理解Java虚拟机(一)之内存布局和对象的创建

    深入理解Java虚拟机系列文章 深入理解Java虚拟机(二)之四种引用 深入理解Java虚拟机(三)之垃圾收集 深...

  • 执行上下文

    原文出处 JavaScript深入之执行上下文 具体执行分析 代码如下: 执行过程如下: 执行全局代码,创建全局执...

  • 【学习笔记】java线程池

    深入理解Java之线程池

  • 谈散文

    纵观古今,散文的存龄可谓庞大,它以轻慢的步伐深入人心。它集诗书之美韵,又富礼乐之谐和,一步一个脚印地深入...

  • 一份收获

    深情萌芽出无数的风景, 而心海扬帆的梦与之同行。 纵观全局之彷徨, 错误的抉择衍生错误的芬芳。 路漫漫而现实以磨难...

  • 深入理解Java虚拟机(二)之四种引用

    深入理解Java虚拟机系列文章 深入理解Java虚拟机(一)之内存模型 深入理解Java虚拟机(三)之垃圾收集 深...

  • 移动前端开发之viewport的深入理解

    移动前端开发之viewport的深入理解

网友评论

    本文标题:深入理解VLC之纵观全局

    本文链接:https://www.haomeiwen.com/subject/qrzokctx.html