libmatroska 与 libebml 移植到 Android 支持 mkv 格式解析与创建

184 阅读4分钟

Matroska 多媒体容器(Multimedia Container)是一种开放标准的自由的容器和文件格式,是一种多媒体封装格式,能够在一个文件中容纳无限数量的视频、音频、图片或字幕轨道。所以其不是一种压缩格式,而是 Matroska 定义的一种多媒体容器文件。其目标是作为一种统一格式保存常见的电影、电视节目等多媒体内容。在概念上 Matroska 和其他容器,比如 AVI、MP4 或 ASF(Advanced Streaming Format,即高级流格式)比较类似,但其在技术规程上完全开放,在实现上包含很多开源软件。可将多种不同编码的视频及 16 条以上不同格式的音频和不同语言的字幕流封装到一个Matroska 媒体文件当中。最大的特点就是能容纳多种不同类型编码的视频、音频及字幕流。

mkv 不同于 DivX、XviD 等视频编码格式,也不同于 MP3、Ogg 等音频编码格式。mkv 是为这些音、视频提供外壳的“组合”和“封装”格式。换句话说就是一种容器格式,常见的 DAT(是 VCD 的一种编码格式)AVl、VOB、MPEG、RM 格式其实也都属于这种类型。但它们要么结构陈旧,要么不够开放,这才促成了 MKV 这类新型多媒体封装格式的诞生。

Matroska 开源多媒体容器标准。MKV 属于其中的一部分。Matroska 常见的有 .MKV 视频格式、MKA 音频格式、.MKS 字幕格式、.MK3D files (stereoscopic/3D video)。MKV 这种多媒体封装是建立在 EBML 语言的基础上,EBML 是可扩展的二进制语言,优势在于其可变长度的整数存储,节省空间。

libmatroska —— 用来解析 Matroska 文件(.mkv和.mka)的 C++ 库

libebml —— 用来解析 EBML 文件的 C++ 库

libmatroska 库除了解析 Matroska 文件,其实还能创建 Matroska 文件,编译 libmatroska 依赖 libebml 库。

一、移植 libebml

  1. 将源码(github.com/Matroska-Or…)复制到 Android Studio main/cpp 路径下

在这里插入图片描述

  1. 修改 CMakeLists.txt 调用 Cmake 编译 libebml
cmake_minimum_required(VERSION 3.4.1)

project(ebml VERSION 1.3.10)

set(libebml_SOURCES
        src/main/cpp/Debug.cpp
        src/main/cpp/EbmlBinary.cpp
        src/main/cpp/EbmlContexts.cpp
        src/main/cpp/EbmlCrc32.cpp
        src/main/cpp/EbmlDate.cpp
        src/main/cpp/EbmlDummy.cpp
        src/main/cpp/EbmlElement.cpp
        src/main/cpp/EbmlFloat.cpp
        src/main/cpp/EbmlHead.cpp
        src/main/cpp/EbmlMaster.cpp
        src/main/cpp/EbmlSInteger.cpp
        src/main/cpp/EbmlStream.cpp
        src/main/cpp/EbmlString.cpp
        src/main/cpp/EbmlSubHead.cpp
        src/main/cpp/EbmlUInteger.cpp
        src/main/cpp/EbmlUnicodeString.cpp
        src/main/cpp/EbmlVersion.cpp
        src/main/cpp/EbmlVoid.cpp
        src/main/cpp/IOCallback.cpp
        src/main/cpp/MemIOCallback.cpp
        src/main/cpp/MemReadIOCallback.cpp
        src/main/cpp/SafeReadIOCallback.cpp
        src/main/cpp/StdIOCallback.cpp
        #[[src/main/cpp/logger.h]])

set(libebml_PUBLIC_HEADERS
        src/main/cpp/ebml/Debug.h
        src/main/cpp/ebml/EbmlBinary.h
        src/main/cpp/ebml/EbmlConfig.h
        src/main/cpp/ebml/EbmlContexts.h
        src/main/cpp/ebml/EbmlCrc32.h
        src/main/cpp/ebml/EbmlDate.h
        src/main/cpp/ebml/EbmlDummy.h
        src/main/cpp/ebml/EbmlElement.h
        src/main/cpp/ebml/EbmlEndian.h
        src/main/cpp/ebml/EbmlFloat.h
        src/main/cpp/ebml/EbmlHead.h
        src/main/cpp/ebml/EbmlId.h
        src/main/cpp/ebml/EbmlMaster.h
        src/main/cpp/ebml/EbmlSInteger.h
        src/main/cpp/ebml/EbmlStream.h
        src/main/cpp/ebml/EbmlString.h
        src/main/cpp/ebml/EbmlSubHead.h
        src/main/cpp/ebml/EbmlTypes.h
        src/main/cpp/ebml/EbmlUInteger.h
        src/main/cpp/ebml/EbmlUnicodeString.h
        src/main/cpp/ebml/EbmlVersion.h
        src/main/cpp/ebml/EbmlVoid.h
        src/main/cpp/ebml/IOCallback.h
        src/main/cpp/ebml/MemIOCallback.h
        src/main/cpp/ebml/MemReadIOCallback.h
        src/main/cpp/ebml/SafeReadIOCallback.h
        src/main/cpp/ebml/StdIOCallback.h)

set(libebml_C_PUBLIC_HEADERS src/main/cpp/ebml/c/libebml_t.h)

add_library(
        ebml

        SHARED

        ${libebml_PUBLIC_HEADERS}
        ${libebml_C_PUBLIC_HEADERS}
        ${libebml_SOURCES})

find_library(
        log-lib

        log)

set_target_properties(ebml PROPERTIES
        VERSION 4.0.0
        SOVERSION 4)

target_include_directories(ebml
        PRIVATE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

include(GenerateExportHeader)
generate_export_header(ebml EXPORT_MACRO_NAME EBML_DLL_API EXPORT_FILE_NAME ebml_export.h)
target_sources(ebml
        PRIVATE
        ${CMAKE_CURRENT_BINARY_DIR}/ebml_export.h)

target_link_libraries(
        ebml

        ${log-lib}
        jnigraphics)
  1. 修改 build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "xxx.xxx"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        externalNativeBuild {
            cmake {
                cppFlags "-frtti -fexceptions -std=c++11"
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

编译成功以后 /build/intermediates/cmake/ 路径下可看到正确编译的 libebml 动态库。

在这里插入图片描述

二、移植 libmatroska

  1. 在 Android Studio 新建一个 Module;
  2. 将源码(github.com/Matroska-Or…)复制到 Android Studio main/cpp 路径下

在这里插入图片描述

  1. 复制 libebml 头文件和编译出的 libebml.so 到此 Module libs 路径下

在这里插入图片描述

  1. 复制 ebml_export.h (.cxx/cmake/debug/armeabi-v7a/ebml_export.h)到 libs/include/ebml/ 路径下,ebml_export.h 是在编译 libebml 时自动生成的,在 Cmake 脚本中配置过

.cxx/cmake/debug/armeabi-v7a/ebml_export.h 路径显示在 Android Studio 中:

在这里插入图片描述

libs/include/ebml/ebml_export.h 路径显示在 Android Studio 中:

在这里插入图片描述
5. 修改 CMakeLists.txt 调用 Cmake 编译 libmatroska

cmake_minimum_required(VERSION 3.4.1)

project(matroska VERSION 1.5.2)

set(distribution_DIR ../../../../libs)

add_library( ebml
        SHARED
        IMPORTED)
set_target_properties( ebml
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libebml.so)

set(libmatroska_SOURCES
        src/main/cpp/FileKax.cpp
        src/main/cpp/KaxAttached.cpp
        src/main/cpp/KaxAttachments.cpp
        src/main/cpp/KaxBlock.cpp
        src/main/cpp/KaxBlockData.cpp
        src/main/cpp/KaxCluster.cpp
        src/main/cpp/KaxContexts.cpp
        src/main/cpp/KaxCues.cpp
        src/main/cpp/KaxCuesData.cpp
        src/main/cpp/KaxInfoData.cpp
        src/main/cpp/KaxSeekHead.cpp
        src/main/cpp/KaxSegment.cpp
        src/main/cpp/KaxSemantic.cpp
        src/main/cpp/KaxTracks.cpp
        src/main/cpp/KaxVersion.cpp
        #[[src/main/cpp/logger.h]])

set(libmatroska_PUBLIC_HEADERS
        src/main/cpp/matroska/FileKax.h
        src/main/cpp/matroska/KaxAttached.h
        src/main/cpp/matroska/KaxAttachments.h
        src/main/cpp/matroska/KaxBlockData.h
        src/main/cpp/matroska/KaxBlock.h
        src/main/cpp/matroska/KaxChapters.h
        src/main/cpp/matroska/KaxClusterData.h
        src/main/cpp/matroska/KaxCluster.h
        src/main/cpp/matroska/KaxConfig.h
        src/main/cpp/matroska/KaxContentEncoding.h
        src/main/cpp/matroska/KaxContexts.h
        src/main/cpp/matroska/KaxCuesData.h
        src/main/cpp/matroska/KaxCues.h
        src/main/cpp/matroska/KaxDefines.h
        src/main/cpp/matroska/KaxInfoData.h
        src/main/cpp/matroska/KaxInfo.h
        src/main/cpp/matroska/KaxSeekHead.h
        src/main/cpp/matroska/KaxSegment.h
        src/main/cpp/matroska/KaxSemantic.h
        src/main/cpp/matroska/KaxTag.h
        src/main/cpp/matroska/KaxTags.h
        src/main/cpp/matroska/KaxTrackAudio.h
        src/main/cpp/matroska/KaxTrackEntryData.h
        src/main/cpp/matroska/KaxTracks.h
        src/main/cpp/matroska/KaxTrackVideo.h
        src/main/cpp/matroska/KaxTypes.h
        src/main/cpp/matroska/KaxVersion.h)

set (libmatroska_C_PUBLIC_HEADERS
        src/main/cpp/matroska/c/libmatroska.h
        src/main/cpp/matroska/c/libmatroska_t.h)

add_library(
        matroska

        SHARED

        ${libmatroska_PUBLIC_HEADERS}
        ${libmatroska_C_PUBLIC_HEADERS}
        ${libmatroska_SOURCES})

include_directories(libs/include)

find_library(
        log-lib

        log)

set_target_properties(matroska PROPERTIES
        VERSION 6.0.0
        SOVERSION 6)

target_include_directories(matroska
        PRIVATE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

include(GenerateExportHeader)
generate_export_header(matroska EXPORT_MACRO_NAME MATROSKA_DLL_API EXPORT_FILE_NAME matroska_export.h)
target_sources(matroska
        PRIVATE
        ${CMAKE_CURRENT_BINARY_DIR}/matroska_export.h)

target_link_libraries(
        matroska

        ${log-lib}
        jnigraphics
        ebml)
  1. 修改 build.gradle
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles 'consumer-rules.pro'

        externalNativeBuild {
            cmake {
                cppFlags "-frtti -fexceptions -std=c++11"
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
  1. 编译的时候会发现头文件报错,把每个头文件中的内容 include matroska 路径都删掉

编译成功以后 /build/intermediates/cmake/ 路径下可看到正确编译的 libmatroska 动态库。

在这里插入图片描述