15AH, San Francisco

California, United States.

Send Your Mail At:

tianyingkejishe@sina.cn

Working Hours

Mon-Sat: 9.30am To 7.00pm

分类标题

Autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et dolore feugait.

分类归档 ffmpeg

【ffmpeg】五、android使用ffmpeg.so

前言:经过前几篇的介绍,我们已经掌握在linux下编译ffmpeg.so。我们编译出so,目的是为了能够使用so达到使用ffmpeg相关功能的目的。本篇介绍如何在android上使用ffmpeg的so库的基础功能。本文要求读者有一定的jni基础。

本文开发环境:

ffmpeg版本:ffmpeg4.2.2

androidStudio:2022.1.1 Patch 1;gradle版本:7.4.1;ndk21(必须要有ndk环境)

一、androidStudio新建jni(C++)项目

二、导入linux生成的ffmepg的7个so文件和头文件

1、在libs目录下新建文件夹arm64-v8a(本文只引入arm64-v8a版本,读者可以参照自行引入armeabi-v7a版本),在其中复制进来ffmepg相关的7个so文件,在cpp目录下导入include的C头文件:

 2、编辑CMakeLists.txt联编脚本

cmake_minimum_required(VERSION 3.22.1)
#自动生成,自己的项目名
project("ffmpegndk")
 
message("start............................................................")
# 1. 定义so库和头文件所在目录相对与脚本的位置的变量,方面后面使用
set(ffmpeg_lib_dir_neway ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/include)
 
# 2. 添加头文件目录
include_directories(${ffmpeg_head_dir})
 
#==========================ffmpeg引入开始====================================
#add_library(配置模块so库编译信息),set_target_properties(为目标配置编译属性),对于三方so,这两个方法要成对出现
add_library( avcodec
        SHARED
        IMPORTED)
set_target_properties(
        avcodec
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libavcodec.so)
 
add_library( avdevice
        SHARED
        IMPORTED)
set_target_properties(
        avdevice
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libavdevice.so)
 
add_library( avfilter
        SHARED
        IMPORTED)
set_target_properties(
        avfilter
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libavfilter.so)
 
add_library( avformat
        SHARED
        IMPORTED)
set_target_properties(
        avformat
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libavformat.so)
 
add_library( avutil
        SHARED
        IMPORTED)
set_target_properties(
        avutil
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libavutil.so)
 
add_library( swresample
        SHARED
        IMPORTED)
set_target_properties(
        swresample
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libswresample.so)
 
add_library( swscale
        SHARED
        IMPORTED)
set_target_properties(
        swscale
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libswscale.so)
#==========================ffmpeg引入结束====================================
 
#对最后一个自己的so(名称可以自定义),不需要再set_target_properties。这个so最终会生成到build中。
add_library(
        ffmpegNdkCustom
        SHARED
        native-lib.cpp)
 
#查找代码中使用到的android系统预留(预构建)库(如log、zlib压缩库等)
find_library(
        log-lib
        log)
 
#指定编译最终目标库时,cmke要链接的库。
#解释:一般形式是:target_link_libraries(your-lib library1 library2 ...)
target_link_libraries(
        #指定目标库
        ffmpegNdkCustom
        #连接 FFmpeg 相关的库
        avcodec
        avdevice
        avfilter
        avformat
        avutil
        swresample
        swscale
        ${log-lib})

 3、编辑native-lib.cpp文件,编写native方法。

下面的 stringFromJNI 是项目默认帮我们生成的默认方法,getConfiguration是我们测试ffmpeg获取版本号的一个方法

#include <jni.h>
#include <string>
extern "C" {
#include "libavcodec/avcodec.h"
}
 
extern "C" JNIEXPORT jstring JNICALL
Java_com_neway_ffmpegndk_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
 
extern "C"
JNIEXPORT jstring JNICALL
Java_com_neway_ffmpegndk_MainActivity_getConfiguration(JNIEnv *env, jobject thiz) {
    return env->NewStringUTF(avcodec_configuration());
}

三、app中使用

1、修改MainActivity方法:

package com.neway.ffmpegndk;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.widget.TextView;
 
import com.neway.ffmpegndk.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
    //用于在程序启动时加载cmake编译好的目标库ffmpegNdkCustom.so,注意大小写
    static {
        System.loadLibrary("ffmpegNdkCustom");
    }
 
    private ActivityMainBinding binding;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        TextView tv = binding.sampleText;
        //tv.setText(stringFromJNI());
        tv.setText(getConfiguration());
    }
 
    /**
     * 在 ffmpegNdkCustom.so 库中声明的原生c++的native方法,起连接作用
     */
    public native String stringFromJNI();
    public native String getConfiguration();
}

2、修改app级别的build.gradle文件,用以使app支持arm64-v8a架构:

android {
    namespace 'com.neway.ffmpegndk'
    compileSdk 33
 
    defaultConfig {
        applicationId "com.neway.ffmpegndk"
        minSdk 21
        targetSdk 33
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'arm64-v8a'//,'armeabi-v7a'//,'x86','arm64-v8a',
            }
        }
    }
    ...
}

3、运行(因为指定了arm64-v8a架构,故不能在电脑模拟器上运行,需要真机,电脑模拟器需要编译使用x86架构的),即展示了我们的ffmpeg版本号:

4、此时,我们也可以观察下,cmake联编的目标so同时也生成在了我们的build目录下:

后记:

本文使用了ffmpeg未合并的7个so文件方式,当然也可以使用合并so(见系列文章第四篇ffmpeg专辑(四):linux编译ffmpeg.so合并so篇)后的ffmpeg.so文件,此时libs文件夹和cmake联编文件需要变更,其余地方保持不变:

cmake_minimum_required(VERSION 3.22.1)
 
project("ffmpegndk")
 
message("start............................................................")
# 1. 定义so库和头文件所在目录,方面后面使用
set(ffmpeg_lib_dir_neway ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/include)
 
# 2. 添加头文件目录
include_directories(${ffmpeg_head_dir})
 
#==========================ffmpeg引入开始====================================
#ffmpegsofile为自定义名称,libffmpeg.so是libs目录下的linux编译出的so文件
add_library( ffmpegsofile
        SHARED
        IMPORTED)
set_target_properties(
        ffmpegsofile
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir_neway}/libffmpeg.so)
#==========================ffmpeg引入结束====================================
 
#对最后一个自己的so,不需要再set_target_properties。这个so最终会生成到build中。
add_library(
        ffmpegNdkCustom
        SHARED
        native-lib.cpp)
 
find_library(
        log-lib
        log)
 
target_link_libraries(
        #指定目标库
        ffmpegNdkCustom
        #连接 FFmpeg 相关的库
        ffmpegsofile
        ${log-lib})

【ffmpeg】四、linux编译ffmpeg.so合并so篇

前言:在前几篇文章已经介绍了,如何进行编译ffmpeg.so的一个完整过程。但是编译出的是7个独立模块的so文件,使用起来较为不便。本文将在上一篇文章的基础上进行so的合并介绍。

一、编写脚本

注意:本文所述依然是双架构编译,但所谓合并so,并不是合并两个架构的so,而是分别在自己的架构下,进行多个ffmpeg的.a静态库合并成动态库so的过程。即本文两个架构来说,这个过程进行了2次。

在前几篇文章中的ffmpeg-4.2.2源码目录下新建一个编译脚本:build_android_v8v7_comileso.sh:

#!/bin/bash
make clean
API=21
NDK=/home/liuzihui/softwaresCus/ndkr21/android-ndk-r21e
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
SYSROOT=$TOOLCHAIN/sysroot
ADDITIONAL_CONFIGURE_FLAG="--enable-avdevice --enable-avcodec --enable-avformat --enable-swresample --enable-swscale --enable-postproc --enable-avfilter"
function build_android {
  echo "===========================1========================"
  ./configure \
  --prefix=$OUTPUT \
  --target-os=android \
  --arch=$ARCH \
  --cpu=$CPU \
  --enable-asm \
  --enable-neon \
  --enable-cross-compile \
  --disable-shared \
  --enable-static \
  --disable-doc \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-symver \
  --disable-ffmpeg \
  --sysroot=$SYSROOT \
  --cross-prefix=$CROSS_PREFIX \
  --cc=$CC \
  --cxx=$CXX \
  --extra-cflags="-fPIC" \
  $ADDITIONAL_CONFIGURE_FLAG
  echo "===========================2====================="
  make clean
  echo "=============================${CC}==============="
  make -j4
  make install
  $COMBILE_TOOLCHAIN_LD \
-rpath-link=$COMBILE_PLATFORM/usr/lib \
-L$COMBILE_PLATFORM/usr/lib \
-L$OUTPUT/lib \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
$OUTPUT/libffmpeg.so \
    libavcodec/libavcodec.a \
    libavfilter/libavfilter.a \
    libswresample/libswresample.a \
    libavformat/libavformat.a \
    libavutil/libavutil.a \
    libavdevice/libavdevice.a \
    libswscale/libswscale.a \
    -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
    $COMBILE_TOOLCHAIN_GCC
}
 
#arm64-v8a
ARCH=arm64
CPU=armv8-a
CPU_INSTRUCT_COMMON=aarch64-linux-android
OUTPUT=/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/$CPU
CROSS_PREFIX=$TOOLCHAIN/bin/$CPU_INSTRUCT_COMMON-    #AR AS LD等通用
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang     #CC单独指定,非通用(因为ndk中CC与AR路径不同,后同理)
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++  #CXX单独指定,非通用
COMBILE_PLATFORM=$NDK/platforms/android-$API/arch-arm64 #
COMBILE_TOOLCHAIN_LD=$NDK/toolchains/$CPU_INSTRUCT_COMMON-4.9/prebuilt/linux-x86_64/bin/$CPU_INSTRUCT_COMMON-ld
COMBILE_TOOLCHAIN_GCC=$NDK/toolchains/$CPU_INSTRUCT_COMMON-4.9/prebuilt/linux-x86_64/lib/gcc/$CPU_INSTRUCT_COMMON/4.9.x/libgcc.a
build_android
 
#armeabi-v7a
ARCH=arm
CPU=armv7-a
CPU_INSTRUCT_COMMON=arm-linux-androideabi
OUTPUT=/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/$CPU
CROSS_PREFIX=$TOOLCHAIN/bin/$CPU_INSTRUCT_COMMON-       #AR AS LD等通用
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang     #CC单独指定,非通用
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++  #CXX单独指定,非通用
COMBILE_PLATFORM=$NDK/platforms/android-$API/arch-arm
COMBILE_TOOLCHAIN_LD=$NDK/toolchains/$CPU_INSTRUCT_COMMON-4.9/prebuilt/linux-x86_64/bin/$CPU_INSTRUCT_COMMON-ld
COMBILE_TOOLCHAIN_GCC=$NDK/toolchains/$CPU_INSTRUCT_COMMON-4.9/prebuilt/linux-x86_64/lib/gcc/$CPU_INSTRUCT_COMMON/4.9.x/libgcc.a
build_android
 

说明:

1、思路是用 –disable-shared –enable-static 先生成7个.a(静态库),然后再利用脚本合并成一个so;

2、在合并so方面,我们在 make install后面追加了一些脚本,虽然ndk17以后在编译ffmpeg.so使用clang,但是合并so这块,适用性更强的gcc在这里派上了用场。

3、-L指定了合并so最终生成的地方,演示项目里(对于armv8a架构来说)是:/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/armv8-a/lib

二、执行脚本

在前几篇文章中的ffmpeg-4.2.2源码目录下,终端命令:赋予脚本权限和执行脚本:

chmod +x build_android_v8v7_comileso.sh
./build_android_v8v7_comileso.sh

【ffmpeg】三、linux编译ffmpeg.so多架构编译【f

前言:在前两章已经介绍了,如何进行编译ffmpeg.so的一个完整过程。但是都是基于一个架构编译的,本文介绍一个sh脚本同时编译出双架构的方法,使编译过程更加方便和高效。

目前android端常用的架构有两个:

arm64-v8a:包含32位和64位执行状态

armeabi-v7a:只包含32位执行状态。

一、编写脚本

在前几篇文章中的ffmpeg-4.2.2源码目录下新建一个编译脚本:build_android_v8v7.sh:

#!/bin/bash
make clean
#变量定义,变量前面的export可要可不要,如export API
API=21
NDK=/home/liuzihui/softwaresCus/ndkr21/android-ndk-r21e
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
SYSROOT=$TOOLCHAIN/sysroot
ADDITIONAL_CONFIGURE_FLAG="--enable-avdevice --enable-avcodec --enable-avformat --enable-swresample --enable-swscale --enable-postproc --enable-avfilter"
function build_android {
  echo "===========================1========================"
  ./configure \
  --prefix=$OUTPUT \
  --target-os=android \
  --arch=$ARCH \
  --cpu=$CPU \
  --enable-asm \
  --enable-neon \
  --enable-cross-compile \
  --enable-shared \
  --disable-static \
  --disable-doc \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-symver \
  --disable-ffmpeg \
  --sysroot=$SYSROOT \
  --cross-prefix=$CROSS_PREFIX \
  --cc=$CC \
  --cxx=$CXX \
  --extra-cflags="-fPIC" \
  $ADDITIONAL_CONFIGURE_FLAG
  echo "===========================2====================="
  make clean
  echo "=============================${CC}==============="
  make -j4
  make install
}
 
#arm64-v8a
ARCH=arm64
CPU=armv8-a
OUTPUT=/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/$CPU
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-    #AR AS LD等通用
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang     #CC单独指定,非通用(因为ndk中CC与AR路径不同,后同理)
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++  #CXX单独指定,非通用
build_android
 
#armeabi-v7a
ARCH=arm
CPU=armv7-a
OUTPUT=/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/$CPU
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-       #AR AS LD等通用
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang     #CC单独指定,非通用
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++  #CXX单独指定,非通用
build_android
 

二、执行脚本

在前几篇文章中的ffmpeg-4.2.2源码目录下,终端命令:赋予脚本权限和执行脚本:

chmod +x build_android_v8v7.sh
./build_android_v8v7.sh

等待几分钟,然后会在ffmpeg-4.2.2源码目录下自动生成android目录和里面的内容:

后记:本次编译没有使用so的裁剪(上一篇已经讲过),使用生成默认的全量包(即生成了7个so)的方式编译的,后面的文章将讲述如何合并这7个so文件。

注意:若想自行裁剪,可以参照上一章介绍,修改本次脚本中的自定义变量 ADDITIONAL_CONFIGURE_FLAG 里面的一些enable和disable属性进行功能模块的配置。

【ffmpeg】二、linux编译ffmpeg.so脚本改良与裁编

前言:在上一章已经介绍了,如何进行编译ffmpeg.so的一个完整过程。但是用的编译脚本较为基础,本章将要介绍如何优化以及ffmpeg的指定模块编译(裁剪)。注意,本系列从第二篇开始,会对脚本结构进行微调,目的是为了大家能举一反三,在使用中做到逐渐熟练其原理。

一、脚本优化

对于脚本优化,这里我将利用脚本中的function关键字进行脚本的提取,与上一篇不同,为了对比学习,这次针对单架构arm64-v8a(向下兼容64和32位)进行编译,脚本如下:

build_androidv8.sh

内容:

#!/bin/bash
make clean
 
API=21
NDK=/home/liuzihui/softwaresCus/ndkr21/android-ndk-r21e
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
SYSROOT=$TOOLCHAIN/sysroot
#function方法名后面一定要有空格,否则报错
function build_android {
echo "===========================1========================"
./configure \
  --prefix=$OUTPUT \
  --target-os=android \
  --arch=$ARCH \
  --cpu=$CPU \
  --enable-asm \
  --enable-neon \
  --enable-cross-compile \
  --enable-shared \
  --disable-static \
  --disable-doc \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-symver \
  --disable-ffmpeg \
  --sysroot=$SYSROOT \
  --cross-prefix=$CROSS_PREFIX \
  --cc=$CC \
  --cxx=$CXX \
  --extra-cflags="-fPIC" \
  $ADDITIONAL_CONFIGURE_FLAG
echo "===========================2========================"
make clean
make
make install
}
 
#arm64-v8a
ARCH=arm64
CPU=armv8-a
OUTPUT=/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/$CPU
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-       #AR AS LD等通用
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang     #CC单独指定,非通用
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++  #CXX单独指定,非通用
ADDITIONAL_CONFIGURE_FLAG= #自定义附件选项,比如加”--disable-xxx –enable-xx”,其中,引号要带上
build_android

注意:这里和上一章不同的是用到了function关键字进行封装,然后部分变量定义(如ARCH、CPU等)放在了下面(其实这样做有别的好处,即多架构编译,后面文章会讲到)。

在上一篇文章中的ffmpeg-4.2.2源码目录下,终端命令:赋予权限和执行脚本:

chmod +x build_androidv8.sh
./build_androidv8.sh

效果:

二、指定模块编译(裁剪)

裁剪的意思是只编译部分我们需要的ffmpeg模块,这样不仅可以减小输出包大小,也能大幅度提高编译速度。

但是在介绍裁剪之前,我们需要了解哪些能裁剪,这就要了解ffmpeg8大模块他们之前的依赖关系:

由此可以看出,avtuil是一个非常底层的模块,不可以不编译,其余模块可以按照他们的依赖关系,进行选择性编译,下面就编译 arm64-v8a 架构下avfilter模块作为示例:

build_androidv8cut.sh

#!/bin/bash
make clean
 
API=21
NDK=/home/liuzihui/softwaresCus/ndkr21/android-ndk-r21e
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
SYSROOT=$TOOLCHAIN/sysroot
 
function build_android {
echo "===========================1========================"
./configure \
  --prefix=$OUTPUT \
  --target-os=android \
  --arch=$ARCH \
  --cpu=$CPU \
  --enable-asm \
  --enable-neon \
  --enable-cross-compile \
  --enable-shared \
  --disable-static \
  --disable-doc \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-symver \
  --disable-ffmpeg \
  --sysroot=$SYSROOT \
  --cross-prefix=$CROSS_PREFIX \
  --cc=$CC \
  --cxx=$CXX \
  --extra-cflags="-fPIC" \
  $ADDITIONAL_CONFIGURE_FLAG
echo "===========================2========================"
make clean
make
make install
}
 
#arm64-v8a
ARCH=arm64
CPU=armv8-a
OUTPUT=/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/$CPU
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-       #AR AS LD等通用
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang     #CC单独指定,非通用
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++  #CXX单独指定,非通用
ADDITIONAL_CONFIGURE_FLAG="--disable-avdevice --disable-avcodec --disable-avformat --disable-swresample --disable-swscale --disable-postproc --enable-avfilter"
#avdevice,avcodec,avformat,swresample,swscale,postproc,avfilter可选不编译(都是默认开启的,avutil基本的,也是默认开启),为了提升编译速度和仅供演示,这里仅开启avfilter
build_android

注意:这里用 ADDITIONAL_CONFIGURE_FLAG 自定义变量指定了可编译选项,具体项目变量可自己定义别的名称,也可以不用该变量,直接写在./configure编译脚本中。

依然在ffmpeg-4.2.2源码目录下,终端命令:赋予权限和执行脚本:

chmod +x build_androidv8cut.sh
./build_androidv8cut.sh

效果:(libvutil.so是基础so,必生成,libavfilter.so即是我们指定模块编译的,且编译过程中我们可以看到,编译速度有大幅提升) 

【ffmpeg】一、linux编译ffmpeg.so入门

前言:在使用android进行音视频开发的时候,ffmpeg是一个利器。但是由于ffmpeg的编译涉及到交叉编译,让很多新手感到头疼。本系列将以多篇文章讲述如何编译so以及多种角度阐述原理。

本文使用环境:

编译环境:ubuntu16.04(在VMware15.5中运行)

ndk版本:android-ndk-r21e

ffmpeg版本:4.2.2

至于ndk版本的一点说明:

ndk:在ndk17以前,都是使用gcc编译,官方从ndk17及以后,全面改用效率更高的clang编译器进行编译。且clang对ffmpeg版本没有很高的要求。

如果ndk选用不当(如ndk17以下使用clang编译),会造成意想不到的错误。本文选用较为稳定的组合进行演示,即:ndk21(clang编译器)+ ffmpeg4.2.2

至于ubuntu登录系统的说明:

本文登录用户按照最常见场景的原则,使用普通用户登录模式,后续linux命令行执行角色都是这个普通用户,非root模式。

步骤一、准备工作

1、进入ubuntu系统桌面,在根目录下新建一个目录,如:/home/liuzihui/softwaresCus,然后新建两个目录,分别用于存放ffmpeg源码和ndk工具包。

注意:若vmware虚拟机里的ubuntu无法联网,也可以从windows里先下载好,再拷贝到wmware虚拟机里的ubuntu中的ffmpegSource目录中来。

ffmpeg网址(4.2.2版本):Download FFmpeg

2、ffmpeg源码下载:

在ffmpegSource目录下,在当前目录的终端(terminal)里使用命令下载ffmpeg4.2.2源码:

wget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.gz

 然后依然在当前目录的终端中,使用命令进行解压:

tar -zxvf ffmpeg-4.2.2.tar.gz

3、ndk使用21Linux版本

可以先从windows里下载好,完成后再放到ubuntu中刚才的(图1)中的ndkr21目录中:

NDK 下载  |  Android NDK  |  Android Developers

在ndkr21目录(图1中新建好的)执行终端解压命令:

unzip android-ndk-r21e-linux-x86_64.zip

说明:在很多教程中,都说ndk需要设置linux的环境变量,其实是不用的,完全可以在后续操作中写明全路径避免掉配置环境变量带来的研究成本。

步骤二:正式编译

经过步骤一,ffmpeg4.2.2和ndkR21的源码和工具包全部已经下载完毕,准备工作已经做好。接下来进行编译的具体讲解。

1、我们进入到(图3)中的 /home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2 目录中,新增一个编译用的脚本文件:build_androidv7.sh(可以先在windows新增并编辑,然后拷贝到该目录下),内容如下:

#!/bin/bash
make clean
#变量定义,变量前面的export可要可不要,如export API
API=21   #选择一个ndk有的,且期望兼容的android版本
ARCH=arm
CPU=armv7-a
OUTPUT=/home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2/android/$CPU #so输出路径,写好进行,编译完成后自动生成该目录
NDK=/home/liuzihui/softwaresCus/ndkr21/android-ndk-r21e
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64 #有的ndk是叫darwin-x86_64,看ndk目录对应
SYSROOT=$TOOLCHAIN/sysroot
 
echo "===========================1========================"
./configure \
  --prefix=$OUTPUT \
  --target-os=android \
  --arch=$ARCH \
  --cpu=$CPU \
  --enable-asm \
  --enable-neon \
  --enable-cross-compile \
  --enable-shared \
  --disable-static \
  --disable-doc \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-symver \
  --disable-ffmpeg \
  --sysroot=$SYSROOT \
  --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
  --cc=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang \
  --cxx=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++ \
  --extra-cflags="-fPIC"
echo "===========================2========================"
make clean
make
make install

我需要对上述sh脚本进行一些讲解:

①API、ARCH、CPU、OUTPUT、NDK、TOOLCHAIN、SYSROOT是脚本语言的一种写法,可以理解为变量定义。网上有的在之前加了export xx,其实可加可不加。其中OUTPUT的输出目录,不需要手动创建,脚本会自行新建;

②echo进行了一个打印,这样做的好处就是在编译时,可以一开始就明显看到哪里脚本哪里没有配置对,利于排障。有一点需要注意,即使我们写错了脚本,但是编译过程虽然报错,但是依然在执行,且生成了一些so文件,这是为什么?是因为他帮我们执行了默认的脚本,这点需要注意,最好多次实践比较下;

③最重要的是cc和cxx的强制指定。因为在有些教程中,提及到了cc_default、cxx_default的问题,甚至要去修改ffmepg源码文件。其实这里大可不必大费周章且可维护性不强,直接在这里指定cc和cxx的路径,可以直接找到位于ndk路径中的cc和cxx的clang编译器(如:/home/liuzihui/softwaresCus/ndkr21/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang)

而cross-prefix前缀是留给AR、AS、LD用的,需要指定下,且脚本中已指定;

④ffmpeg4.2.2源码中已经支持直接指定target-os=android,而非旧版本的linux,故无需再修改configure文件中的SLIBNAME_WITH_MAJOR命名;

⑤本脚本只对armeabi-v7a架构进行了编译,若要编译arm64-v8a,请关注博主后续分享;

⑥在windows编辑好的build_androidv7.sh文件,拷贝到linux中,一般会遇到dox文件格式转unix文件格式的问题(比如报错:/bin/bash^M: bad interpreter),解决方案:

i、方案一:sed -i ‘s/\r//’ build_androidv7.sh;

ii、方案二:可以先vim该脚本文件,然后执行“: set ff=unix”,然后:wq保存。若在编译的时候,还是有问题,这时候可以右击该sh文件,选择open with gedit打开,再保存一下;

⑦请注意,脚本内容中./configure后续的命令项中不要加任何注释,否则在某些系统环境中会出问题,且要确保\符号后面没有多余不可见的空格字符串;

⑧本次sh脚本,会编译出libavdevice,libavcodec,libavfilter,libavformat,libavutil,libswresample,libswscale共7个so文件,后续文章会讲解如何进行so裁剪。

2、脚本写好后,接下来就是编译了(注意,要在 /home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2 目录下执行terminal终端命令,如下图)

①赋予build_androidv7.sh、configure两个文件的可执行权限:

chmod +x build_androidv7.sh
chmod +x configure

  ②执行编译脚本:

./build_androidv7.sh

③等待几分钟,便可以看到 /home/liuzihui/softwaresCus/ffmpegSource/ffmpeg-4.2.2 目录下自动生成了android目录,且lib目录里有我们需要的so文件,同时ffmpeg-4.2.2目录下也自动生成了本次编译生成的宏头文件config.h,里面记录了脚本过程:

④若想重新编译,先删除android目录,再次执行./build_androidv7.sh命令即可。ctrl+C快捷键可以中断编译。

【ffmpeg】使用FFmpeg和FFplay实现UDP流媒体的推送与拉取:单播、组播与广播【ffmpeg】

一、单播

ffmpeg -re -stream_loop -1 -i 123.mp4 -f mpegts  "udp://localhost:12345"
ffplay  udp://localhost:12345

二、多播

ffmpeg -re -stream_loop -1 -i 123.mp4 -f mpegts  "udp://localhost:12345"?multicast=1
ffplay  udp://localhost:12345?multicast=1

三、广播

ffmpeg -re -stream_loop -1 -i 123.mp4 -f mpegts  "udp://255.255.255.255:12345"?multicast=1
ffplay  udp://255.255.255.255:12345?multicast=1