概述
VLC 媒体播放器作为一款由志愿者开发团队精心维护的自由、开源且跨平台的多媒体播放器,能轻松驾驭绝大多数多媒体文件,无论是本地磁盘中的视频、音频,还是来自网络的流媒体协议. VLC for Android 支持网络串流,无论是基于 HLS 的自适应流媒体,还是 DASH 流,都能稳定播放。对于有 NAS(网络附属存储)设备,或者需要访问共享驱动器的用户而言,它也提供了便捷的浏览支持。在兼容性方面,其早期版本支持 Android 2.2 及以上,而当前版本则要求 Android 4.2 及更高版本,同时对 ARM v7、ARMv8/A arch 64、MIPS 和 x86 等多种硬件架构提供支持,甚至还兼容 Android TV ,大大拓展了其使用场景。
编译环境
- 系统: ubuntu 22.04
- ndk: 27
- gradle: 8.13
- cmake: 4.0.3
编译
环境依赖
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
编译命令:
# 设置AndroidSDK 和AndroidNDK
export ANDROID_NDK=~/Android/Sdk/ndk/27.0.12077973/
export ANDROID_SDK=~/Android/Sdk/
./buildsystem/compile.sh -l -a arm64
一些问题
1. cmake 3.22 太旧
编译vlc-android出错:-- The C compiler identification is Clang 18.0.1
-- The CXX compiler identification is Clang 18.0.1
CMake Warning (dev) at path-to-Android/Sdk/ndk/27.0.12077973/build/cmake/flags.cmake:46 (if):
Policy CMP0057 is not set: Support new IN_LIST if() operator. Run "cmake
--help-policy CMP0057" for policy details. Use the cmake_policy command to
set the policy and suppress this warning.
IN_LIST will be interpreted as an operator when the policy is set to NEW.
Since the policy is not set the OLD behavior will be used.
Call Stack (most recent call first):
/usr/share/cmake-3.22/Modules/Platform/Android-Clang.cmake:23 (include)
/usr/share/cmake-3.22/Modules/Platform/Android-Clang-C.cmake:1 (include)
/usr/share/cmake-3.22/Modules/CMakeCInformation.cmake:48 (include)
CMakeLists.txt:3 (project)
This warning is for project developers. Use -Wno-dev to suppress it.
CMake Error at /home/anson/Android/Sdk/ndk/27.0.12077973/build/cmake/flags.cmake:46 (if):
if given arguments:
"hwaddress" "IN_LIST" "ANDROID_SANITIZE"
Unknown arguments specified
Call Stack (most recent call first):
/usr/share/cmake-3.22/Modules/Platform/Android-Clang.cmake:23 (include)
/usr/share/cmake-3.22/Modules/Platform/Android-Clang-C.cmake:1 (include)
/usr/share/cmake-3.22/Modules/CMakeCInformation.cmake:48 (include)
CMakeLists.txt:3 (project)
下载新版本cmake 编译安装
2. /usr/bin/install: cannot stat ‘…/…/share/vlc.appdata.xml’: No such file or directory
cp vlc/share/vlc.appdata.xml.in.in vlc/share/vlc.appdata.xml
3. kotlin-compiler-embeddable-1.6.10.jar 下载太慢
修改源:libvlcjni/build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.android_plugin_version = '8.11.0'
ext.kotlin_version = '1.6.10'
ext.kotlinx_version = '1.6.0'
repositories {
flatDir dirs: "gradle/plugins"
maven { url "https://maven.aliyun.com/repository/public" } // 阿里云镜像
maven { url "https://maven.aliyun.com/repository/gradle-plugin" } // 阿里云插件仓库
mavenCentral() // 保留官方仓库作为 fallback
google()
}
dependencies {
classpath "com.android.tools.build:gradle:$android_plugin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.25.2'
}
}
allprojects {
repositories {
maven { url "https://maven.aliyun.com/repository/public" } // 阿里云镜像
maven { url "https://maven.aliyun.com/repository/gradle-plugin" } // 阿里云插件仓库
mavenCentral()
google()
}
tasks.withType(Javadoc) {
// Ignores errors from mavenAndroidJavadocs task
// (reference: github.com/novoda/bintray-release/issues/71#issuecomment-164324255)
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('encoding', 'UTF-8')
}
}
ext {
libvlcVersion = '3.6.2'
androidxLegacyVersion = '1.0.0'
androidxAnnotationVersion = '1.7.1'
}
使用(aar)播放.H264参考代码
输出文件: libvlcjni/libvlc/build/outputs/aar/libvlc-dev.aar
build.gradle
dependencies {
implementation files("path-to-vlc-android/libvlcjni/libvlc/build/outputs/aar/libvlc-dev.aar");
}
h264_player.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
H264Player.java
import android.net.Uri;
import android.os.Bundle;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.ansondroider.acore.BaseActivity;
import com.ansondroider.acore.Logger;
import com.nmbb.vlc.R;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.libvlc.MediaPlayer;
import org.videolan.libvlc.interfaces.IMedia;
import java.util.ArrayList;
public class H264Player extends BaseActivity {
SurfaceView sv;
String path = "/sdcard/test.h264";
MediaPlayer vlcMp;
LibVLC libVLC;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.h264_player);
sv = (SurfaceView) findViewById(R.id.sv);
sv.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
playH264();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) { }
});
if(!isPermissionReady(getRequiredPermissions()))
getPermissions(getRequiredPermissions(), REQ_PERMS);
}
void playH264() {
Logger.d(TAG, "playH264");
if(libVLC == null) {
// 初始化 LibVLC(可添加额外配置参数)
ArrayList<String> options = new ArrayList<>();
options.add("--no-drop-late-frames"); // 不丢弃延迟帧
options.add("--no-skip-frames"); // 不跳过帧
libVLC = new LibVLC(this, options);
vlcMp = new MediaPlayer(libVLC);
vlcMp.setEventListener(new MediaPlayer.EventListener() {
@Override
public void onEvent(MediaPlayer.Event event) {
Logger.d(TAG, "onEvent: " + event.type);
switch (event.type) {
case MediaPlayer.Event.EndReached:
Logger.d(TAG, "播放结束");
break;
case MediaPlayer.Event.EncounteredError:
Logger.e(TAG, "遇到错误:" + event.getRecordPath());
break;
}
}
});
//设置显示的控件
vlcMp.getVLCVout().setVideoView(sv);
vlcMp.getVLCVout().attachViews();
}
// 创建媒体对象并指定格式参数
Media media = new Media(libVLC, Uri.parse("file://" + path));
// 关键:强制指定为 H.264 格式和解复用器
media.addOption(":input-format=h264"); // 输入格式为 h264
media.addOption(":demux=h264"); // 使用 h264 解复用器
// 额外添加解码器选项(强制使用FFmpeg解码)
//media.addOption(":codec=avcodec");
media.setHWDecoderEnabled(true, false); // 启用硬件解码(可选)
media.setEventListener(new Media.EventListener() {
@Override
public void onEvent(Media.Event event) {
Logger.d(TAG, "onEvent: " + event.type);
if(event.type == Media.Event.MetaChanged){
Logger.d(TAG, "媒体元数据改变");
int trackCount = media.getTrackCount();
for(int i = 0; i < trackCount; i++){
IMedia.Track track = media.getTrack(i);
if(track instanceof IMedia.VideoTrack){
Logger.d(TAG, "视频轨道:" + track.type);
Logger.d(TAG, "Size: " + ((IMedia.VideoTrack) track).width + "x" + ((IMedia.VideoTrack) track).height);
}
}
}
}});
// 设置媒体并播放
vlcMp.setMedia(media);
media.release(); // 释放 media 对象
vlcMp.play();
}
@Override
protected void onDestroy() {
super.onDestroy();
// 释放资源
vlcMp.stop();
vlcMp.release();
libVLC.release();
}
}