VLC 播放器框架

VLC媒体播放器(以前是VideoLAN Client,通常称为VLC)是由VideoLAN项目开发的免费,开源,可移植,跨平台的 媒体播放器软件和流媒体 服务器。VLC适用于台式机操作系统和移动平台,例如Android,iOS, iPadOS, Tizen,Windows 10 Mobile和Windows Phone。VLC也可用于数字分发平台,例如Apple的App Store,Google Play和Microsoft Store。

VLC支持许多音频和视频压缩方法和文件格式,包括DVD视频,视频CD和流协议。它能够通过计算机网络流式传输媒体,并可以对多媒体文件进行转码。

VLC可以读取多种格式,具体取决于运行的操作系统,包括:

容器格式:3GP,ASF,AVI,DVR-MS,FLV,Matroska(MKV),MIDI,QuickTime文件格式,MP4,Ogg,OGM,WAV,MPEG-2(ES,PS,TS, PVA,MP3),AIFF,原始音频,原始DV,MXF,VOB,RM,蓝光,DVD视频,VCD,SVCD,CD音频,DVB,HEIF,AVIF

音频编码格式:AAC, AC3, ALAC, AMR,DTS, DV Audio, XM, FLAC, It, MACE, MOD, Monkey's Audio, MP3, Opus,PLS, QCP, QDM2/QDMC, RealAudio,Speex, Screamtracker 3/S3M, TTA, Vorbis, WavPack, WMA (WMA 1/2, WMA 3 partially).

捕获设备:Video4Linux(在Linux上),DirectShow(在Windows上),台式机(屏幕录像),数字电视(DVB-C,DVB-S,DVB-T,DVB-S2,DVB-T2,ATSC,Clear QAM)

网络协议:FTP,HTTP,MMS,RSS / Atom,RTMP,RTP(单播或多播),RTSP,UDP,Sat-IP,Smooth Streaming

网络流格式: Apple HLS, Flash RTMP, MPEG-DASH, MPEG Transport Stream, RTP/RTSP ISMA/3GPP PSS, Windows Media MMS Subtitles: Advanced SubStation Alpha, Closed Captions, DVB, DVD-Video, MPEG-4 Timed Text, MPL2,OGM, SubStation Alpha, SubRip, SVCD, Teletext,Text file, VobSub, WebVTT, TTML

视频编码格式:Cinepak, Dirac, DV, H.263, H.264/MPEG-4 AVC, H.265/MPEG HEVC, AV1, HuffYUV, Indeo 3, MJPEG, MPEG-1, MPEG-2, MPEG-4 Part 2, RealVideo 3&4,Sorenson, Theora, VC-1, VP5, VP6, VP8, VP9,DNxHD, ProRes and some WMV.

数码摄像机格式:MOD and TOD via USB.

VLC架构

介绍文档

libvlc_stack

VLC元数据

通常元数据(Metadata)指描述有关其它数据信息的数据。

可以将元数据看作是物体内部信息的高级摘要。对于一本书而言,这些数据将由作者、标题、发布日期等组成。而对于多媒体,元数据是指标题、专辑、艺术家、年代等信息。

之所以需要元数据,是因为这能够让物体更容易地被识别、查找、管理和发现。假如你正在搜索特定类型的曲目,使用元数据过滤会十分方便,将艺术家设置为“周杰伦”或者将流派设置为“摇滚”,这时歌曲列表中只会显示周杰伦的曲目或者摇滚类型的曲目。

libVLC 中,元数据类型由枚举类型 libvlc_meta_t 表示

/** Meta data types */
typedef enum libvlc_meta_t {
    libvlc_meta_Title,        // 标题
    libvlc_meta_Artist,       // 艺术家 
    libvlc_meta_Genre,        // 流派 
    libvlc_meta_Copyright,    // 版权
    libvlc_meta_Album,        // 专辑
    libvlc_meta_TrackNumber,  // 轨道编号
    libvlc_meta_Description,  // 描述
    libvlc_meta_Rating,       // 评分
    libvlc_meta_Date,         // 日期
    libvlc_meta_Setting,      // 设置
    libvlc_meta_URL,          // 地址
    libvlc_meta_Language,     // 语言 
    libvlc_meta_NowPlaying,   // 正在播放
    libvlc_meta_Publisher,    // 发行商
    libvlc_meta_EncodedBy,    // 编码者
    libvlc_meta_ArtworkURL,   // 专辑图片地址
    libvlc_meta_TrackID,      // 轨道 ID
    libvlc_meta_TrackTotal,   // 轨道总数
    libvlc_meta_Director,     // 导演
    libvlc_meta_Season,       // 第几季
    libvlc_meta_Episode,      // 插曲
    libvlc_meta_ShowName,     // 显示名
    libvlc_meta_Actors,       // 表演者
    libvlc_meta_AlbumArtist,  // 专辑艺术家
    libvlc_meta_DiscNumber,   // 碟号
    libvlc_meta_DiscTotal     // 总碟数
    /* Add new meta types HERE */
} libvlc_meta_t;

使用 libvlc_media_get_meta() 读取媒体的元数据,但在读取前需先调用 libvlc_media_parse() 解析媒体文件;通过 libvlc_media_set_meta() 设置媒体的元数据,然后调用 libvlc_media_save_meta() 保存设置结果。

VLC事件机制

libVLC 是异步播放媒体文件,在此过程中,它会产生一些事件。例如,可通过监听 libvlc_MediaPlayerEndReached 事件来实现循环播放(当媒体播放结束时获得通知,然后再次播放)。

具体可使用 libvlc_media_player_event_manager() 函数创建事件管理器,然后调用 libvlc_event_attach() 函数将事件处理程序与某个事件相关联。

VLC 轨道信息

一个视频文件看起来是一个整体,但实际上一般会被分为音频和视频两部分。例如将电影导入视频剪辑软件,可以很容易地看到音频和视频是分离的(视频在视频轨道上,音频在音频轨道上)。

libVLC中,媒体轨道信息由结构体libvlc_media_track_t表示:

typedef struct libvlc_media_track_t
{
    /* Codec fourcc */
    uint32_t    i_codec;            // 编解码器
    uint32_t    i_original_fourcc;  // 四字符代码
    int         i_id;               // 轨道 ID
    libvlc_track_type_t i_type;     // 类型

    /* Codec specific */
    int         i_profile;         // 编码级别
    int         i_level;           // 级别的等级

    union {
        libvlc_audio_track_t *audio;        // 音频轨道
        libvlc_video_track_t *video;        // 视频轨道
        libvlc_subtitle_track_t *subtitle;  // 字幕轨道
    };

    unsigned int i_bitrate;  // 码率
    char *psz_language;      // 语言
    char *psz_description;   // 描述

} libvlc_media_track_t;

轨道类型由枚举 libvlc_track_type_t 表示:

typedef enum libvlc_track_type_t
{
    libvlc_track_unknown   = -1,    // 未知
    libvlc_track_audio     = 0,     // 音频
    libvlc_track_video     = 1,     // 视频
    libvlc_track_text      = 2      // 文字
} libvlc_track_type_t;

音频轨道由 libvlc_audio_track_t 表示:

typedef struct libvlc_audio_track_t
{
    unsigned    i_channels;    // 声道
    unsigned    i_rate;        // 采样率  
} libvlc_audio_track_t;

视频轨道由 libvlc_video_track_t 表示:

typedef struct libvlc_video_track_t
{
    unsigned    i_height;            // 视频高度
    unsigned    i_width;             // 视频宽度
    unsigned    i_sar_num;           // 像素宽高比的分子   
    unsigned    i_sar_den;           // 像素宽高比的分母
    unsigned    i_frame_rate_num;    // 帧率的分子
    unsigned    i_frame_rate_den;    // 帧率的分母

    libvlc_video_orient_t       i_orientation;    // 方向
    libvlc_video_projection_t   i_projection;
    libvlc_video_viewpoint_t    pose; /**< Initial view point */
} libvlc_video_track_t;

倘若要计算帧率(FPS),可以通过 i_frame_rate_num/i_frame_rate_den 获得

字幕轨道由 libvlc_subtitle_track_t 表示:

typedef struct libvlc_subtitle_track_t
{
    char *psz_encoding;  // 编码
} libvlc_subtitle_track_t;

VLC API文档

Android

编译库

可以打开VLC Android的 官方仓库 或者 Github仓库 查看源码

该项目是一个Android平台的VLC播放器的APP,并没有将用于播放的libvlc的Java接口绑定像iOS一样单独的正式的发布出来,因此如果我们需要最新版本的话,最好自行进行编译。根据官方说明,应使用Debian/Ubuntu平台进行编译。具体可查看关于 Android平台的详细编译指南

在编译前,需要进行一些准备,以Ubuntu为例,首先安装用于编译的依赖环境

sudo apt install automake ant autopoint cmake build-essential libtool-bin \
    patch pkg-config protobuf-compiler ragel subversion unzip git \
    openjdk-8-jre openjdk-8-jdk flex python wget

然后配置Android的命令行开发环境(SDK、NDK)

请参考《SDK/NDK命令行环境》

接下来需要配置环境变量,打开配置文件sudo vim /etc/profile

export ANDROID_SDK=/home/arcticfox/android-sdk
export ANDROID_NDK=$ANDROID_SDK/ndk/21.3.6528147

Ubuntu上直接使用apt install安装JDK后,可使用命令update-alternatives --config java 搜索出JDK的真正安装路径:

There is only one alternative in link group java (providing /usr/bin/java): /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java

这里的/usr/lib/jvm/java-8-openjdk-amd64即为真实路径,将其配置到环境变量中

export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

最后下载vlc-android的源码(不要拉取Github上的仓库,编译可能存在错误)

git clone https://code.videolan.org/videolan/vlc-android.git

进入到vlc-android目录进行编译

sh buildsystem/compile.sh -l -a all -r

经过漫长的编译(可能需要几个小时),完成后会在libvlc/build/outputs中生成aar

    [javac] warning: [options] bootstrap class path not set in conjunction with -source 5
    [javac] error: Source option 5 is no longer supported. Use 6 or later.
    [javac] error: Target option 1.5 is no longer supported. Use 1.6 or later.

注意,如果编译存在以上Java相关错误,执行java -version检查一下Java版本,新版本的Ubuntu默认带有一个 openjdk-11的版本,优先级可能高于我们安装的openjdk-8,但VLC的库指定使用openjdk-8,因此需要先卸载 openjdk-11

sudo apt-get purge openjdk-11-*

然后使用命令dpkg --list | grep -i jdk查看一下当前的安装情况

项目中集成编译好的 aar 包

在工程根目录下的app路径下,创建libs文件夹,并将 aar 包拷贝进去

配置libs目录

android {
    ...
    repositories{
        flatDir{
            dirs 'libs'
        }
    }
}

添加本地依赖

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    //使用本地aar
    implementation(name: 'libvlc-all-3.3.14', ext: 'aar')
}

vlc-android 快速集成

大多数时候,我们不会对VLC做修改或者深度定制,因此我们可以使用VLC内部的一个gradle仓库,直接从仓库拉取依赖,快速集成,而不是自己编译。

我们可以 访问仓库地址 查看最新的版本号

在工程的根build.gradle文件中配置仓库地址:

allprojects {
    repositories {
        google()
        maven {
            url "https://dl.bintray.com/videolan/Android"
        }

        jcenter()
    }
}

在模块的build.gradle文件中配置依赖

dependencies {
    // ......

    // 这里指定的是目前最新的3.3.14版本,大家根据实际情况替换
    implementation 'org.videolan.android:libvlc-all:3.3.14'
}

最后同步一下gradle,下载依赖

关于该库的简单用法,可以参考VLC官方专门提供的一个 示例工程

public class MainActivity extends AppCompatActivity {

    private MediaPlayer mMediaPlayer;
    TextureView mTextureView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextureView = findViewById(R.id.video);
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
                initPlayer(surfaceTexture);
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
        });
    }

    private void initPlayer(SurfaceTexture surface) {
        ArrayList<String> args = new ArrayList<>();
        // 给出尽可能多的调试日志输出
        args.add("-vvv");

        LibVLC libVLC = new LibVLC(this, args);
        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }

        mMediaPlayer = new MediaPlayer(libVLC);
        // 设置视频输出的Surface
        mMediaPlayer.getVLCVout().setVideoSurface(surface);
        // 播放前必须调用,附加视图
        mMediaPlayer.getVLCVout().attachViews();
        mMediaPlayer.setScale(0);
        mMediaPlayer.setAspectRatio(mTextureView.getWidth() + ":" + mTextureView.getHeight());
        mMediaPlayer.getVLCVout().setWindowSize(mTextureView.getWidth(), mTextureView.getHeight());

        // 网络视频地址
        Media media = new Media(libVLC, Uri.parse("https://v-cdn.zjol.com.cn/277003.mp4"));
        mMediaPlayer.setMedia(media);
        media.release();
        mMediaPlayer.play();
    }
}

扩展Android接口

由于libVLC的安卓包装尚未暴露截屏功能接口,只能手动扩展,然后自行编译打包。

  • 暴露截屏功能

libvlc/jni/libvlcjni-mediaplayer.c里面增加JNI函数

jboolean
Java_org_videolan_libvlc_MediaPlayer_nativeTakeSnapShot(JNIEnv *env, jobject thiz,jint number, jstring path, jint width,jint height)
{

      vlcjni_object *p_obj = VLCJniObject_getInstance(env, thiz);
      /* Get C string */
      const char* psz_path = (*env)->GetStringUTFChars(env, path, 0);
      int res = libvlc_video_take_snapshot(p_obj->u.p_mp, (int)number,psz_path , (int)width,(int)height);
      if(res == 0) return true;
      LOGE("libvlc_video_take_snapshot(%s,%d)", psz_path, res);
      return false;
}

libvlc/src/org/videolan/libvlc/MediaPlayer.java增加native方法和包装方法

/*
 * number:视频标识(默认为0)
 * path:保存路径
 * width:宽(0代表原始宽)
 * height:高(0代表原始高)
 */
public boolean takeSnapShot(int number, String path, int width, int height) {
   return nativeTakeSnapShot(number, path, width, height);
}

private native boolean nativeTakeSnapShot(int number, String path, int width, int height);

iOS

VLCKit库是VLC在MacOS上的绑定,iOS上则是MobileVLCKit。其API 文档见 这里

注意,VLC 对iOS的绑定未暴露CVPixelBufferRef,见issues,因此在Flutter中无法使用外接纹理的方式实现播放器插件。

MobileVLCKit 的基本使用示例:

#import <MobileVLCKit/MobileVLCKit.h>

- (void)initVLCPlayer{
    int width = self.view.bounds.size.width;
    UIView *video = [[UIView alloc]initWithFrame:CGRectMake(0, 0, width, 250)];
    [self.view addSubview:video];
    player = [[VLCMediaPlayer alloc]initWithOptions:nil];
    player.drawable = video;
    char str[20]={0};
    sprintf(str, "%d:250",width);
    [player setVideoAspectRatio: str];
    [player setScaleFactor:0];

    player.media = [VLCMedia mediaWithURL:[NSURL URLWithString:@"https://v-cdn.zjol.com.cn/276996.mp4"]];
}

公众号“编程之路从0到1”

20190301102949549

Copyright © Arcticfox 2021 all right reserved,powered by Gitbook文档修订于: 2022-05-01 12:20:20

results matching ""

    No results matching ""