vlc-android: 编译自己的libvlc

在这里插入图片描述

概述

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

参考

  1. vlc-android
  2. Ubuntu 16.04 Compile VLC for Android Failed
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值