当前位置: 首页 > news >正文

Android 中通过 FFmpeg 命令对音视频编辑处理(已开源)

视音频编辑器

前言

有时候我们想对音视频进行加工处理,比如视频编辑、添加字幕、裁剪等功能处理,虽然 Github 上开源了一些比较不错的项目,但是如果我们想在此项目上进行二次开发,比如我想拿到该项目的动态库基于 OpenH264 来进行对 YUV 编码,这个时候有可能该动态库没有集成 OpenH64 库,所以为了扩展性,我就自己弄了一套万能的库,基本上包含了所有常用的音视频处理库,你不用再去进行编译。

编译完成的头文件和动态库可以在该项目的 core/cpp 目录自行获取(「已完全开源」)。

ps:这里再推荐一下我的另一个音视频处理库 AVEditor ----> 功能正在完善中,可以先关注 ???? 。

AVEditor 是一款短视频编辑 SDK,仿 DouYin 音视频处理。功能包含有美颜、滤镜、贴纸、特效、录制、分段录制、速率录制、变声、配乐、rtmp 直播推流、图片转视频、剪辑,mp4/flv 格式封装等功能。

介绍

「视音频编辑器」 主要移植  FFmpeg v4.4-dev + libx264 + freetype + fontconfig + fribidi + openh264 +libfdk-aac + gnutls + speex + libwebp + lame +opus + opencore-amr + https 等库编译的适用于 Android 平台的音视频编辑、视频剪辑的快速处理框架,目前内置了音视频剪辑、编辑、多个视频文件合并、字幕、水印、倒放等功能,也可以传入 「FFmpeg」 命令来进行处理。

「我们先来看一下内置功能的效果:」

音视频剪辑:

音视频合成:

音视频编辑:

> 更多效果点击阅读原文进行查看


如何使用

1. 添加依赖

implementation 'com.devyk.ffmpeglib:AVFFmpegCore:1.0.1'

2. 功能 API 介绍

「回调处理」

public interface ExecuteCallback {
  /**
  *开始处理
  */
    void onStart(Long executionId);
    /**
     * 如果外部传递了当前操作视频的时长,那么返回的是百分比进度,反之返回的是操作视频对应的微妙时长
     *
     * @param v
     */
    void onProgress(float v);
  /**
  *处理成功
  */
    void onSuccess(long executionId);
  /**
  *处理失败
  */
    void onFailure(long executionId, String error);
  /**
  *取消处理
  */
    void onCancel(long executionId);
  /**
  * ffmpeg 执行的 log
  */
    void onFFmpegExecutionMessage(LogMessage logMessage);
}

「AVEditor」

  • 剪辑

    AVVideo:
    //start:开始的时间 单位秒
    //duration: 剪辑多少秒
    fun clip(start: Float, duration: Float)
    
    AVEditor:
    fun exec(
      epVideo: AVVideo, 
      outputOption: OutputOption, 
      executeCallback: ExecuteCallback)
    
  • 旋转

    AVVideo:
    //rotation:旋转角度(仅支持90,180,270度旋转)
    //isFlip:是否镜像
    fun rotation(rotation: Int, isFlip: Boolean)
    AVEditor:
    fun exec(
      epVideo: AVVideo, 
      outputOption: OutputOption, 
      executeCallback: ExecuteCallback)
    
  • 裁剪

    AVVideo:
    //width: 裁剪的宽
    //height: 裁剪的高
    //x: 从 x 点开始
    //y: 从 y 点开始
    fun crop(
      width: Float, 
      height: Float, 
      x: Float, y: Float)
    AVEditor:
    fun exec(
      epVideo: AVVideo, 
      outputOption: OutputOption, 
      executeCallback: ExecuteCallback)
    
  • 添加文字水印

    AVVideo:
    fun addText(avText: AVText)
    AVEditor:
    fun exec(
      epVideo: AVVideo, 
      outputOption: OutputOption, 
      executeCallback: ExecuteCallback)
    
  • 添加图片水印

    AVVideo:
    fun addDraw(epDraw: AVDraw)
    AVEditor:
    fun exec(
      epVideo: AVVideo, 
      outputOption: OutputOption, 
      executeCallback: ExecuteCallback)
    
  • 视频合并

    AVEditor:
    fun merge(
      epVideos: List<AVVideo>, 
      outputOption: OutputOption, 
      executeCallback: ExecuteCallback)
    
  • 添加背景音乐

    AVEditor:
    music(
         videoin: String,
            audioin: String,
            output: String,
            videoVolume: Float,
            audioVolume: Float,
            executeCallback: ExecuteCallback
        ) 
    
  • 音视频分离

    AVEditor:
    fun demuxer(
      inSource: String, outSource: String, 
      format: Format, 
      executeCallback: ExecuteCallback)
    
  • 视频倒放

    AVEditor:
    fun reverse(
      videoin: String, out: String, 
      vr: Boolean,//视频是否倒放
      ar: Boolean, //音频是否倒放
      executeCallback: ExecuteCallback)
    
  • 视频转图片

    AVEditor:
    fun video2pic(
      videoin: String, //视频输入文件
      out: String,  //图片输出路径-目录
      w: Int, h: Int, //输出图片的宽高
      rate: Float, //每秒视频生成图片数
      executeCallback: ExecuteCallback)
    
  • 视频转 Gif

    AVEditor:
    fun video2Gif(
            videoin: String,
            gifOut: String,
            startDuration: Int,
            stopDuration: Int,
            executeCallback: ExecuteCallback
        )
    
  • 自定义命令

    AVEditor:
    //cmd:FFmpeg 命令
    //duration: 处理视频的时长,可以通过 VideoUitls.getDuration(videoPath) 来获取
    fun execCmd(cmd: String, duration: Long, executeCallback: ExecuteCallback) 
    

FFmpeg 编译小技巧

有时候我们发现 Github 上一些基于 FFmpeg 开发的比较好的项目,比如 ijkplayer ,RxFFmpeg 等,我们想基于它做二次开发,由于我们不知道怎么编译,也不知道编译 FFmpeg 到底需要开启哪些节点,这个时候我就想拿某些项目的编译脚本,基于它来进行二次编译。一般来说有些项目不会开源编译 FFmpeg 的脚本。这个时候我们可以通过拿到开源项目的静态或者动态库,这里我就以 RxFFmpeg  来举例,可以看看我是如果拿到它的编译脚本.

1、先 clone  RxFFmpeg

git clone https://github.com/microshow/RxFFmpeg.git

2、关联 librxffmpeg-core.so

通过该 so  我们知道它应该就是 FFmpeg 编译之后的动态库,现在我们通过 cmake 的方式关联到该 so

cmake_minimum_required(VERSION 3.4.1)
#JNI 路径
set(FFMpeg_include_PATH ${CMAKE_SOURCE_DIR})
include_directories(${FFMpeg_include_PATH}/include/)
add_library(RxFFmpeg SHARED IMPORTED)
set_target_properties(RxFFmpeg PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../libs/${CMAKE_ANDROID_ARCH_ABI}/librxffmpeg-core.so)
find_library(
        log-lib
        log)
FILE(GLOB JNI_ALL_C ${JNI_PATH}/*.cpp)
add_library(
        ffmpeg-tools
        SHARED
        ${JNI_ALL_C}
)
target_link_libraries(
        ffmpeg-tools
        RxFFmpeg
${log-lib}
)

3、编写 JNI 函数,拿到编译脚本

//
// Created by DevYK on 2020-10-02.
//
#include <android/log.h>
extern "C"
{
#include "libavutil/avutil.h"
}
#include <jni.h>
#define  AV_TAG   "AVLOG"
#define LOGE(format, ...)  __android_log_print(ANDROID_LOG_ERROR, AV_TAG, format, ##__VA_ARGS__)
int JNI_OnLoad(JavaVM *javaVM, void *pVoid) {
    const char *config = avutil_configuration();
    LOGE("FFMPEG VERSION%s \n", av_version_info());
    LOGE("FFMPEG configuration %s \n", avutil_configuration());
    return JNI_VERSION_1_6;
}

通过 debug 查看 config 指针指向内存中的信息如下:

嗯,拿到了它的编译信息,然后我们就可以基于它来完善我们项目的编译,我们可以编译出比它的功能更加丰富,就如开头介绍一般,我添加了市面上常用的一些 C++ 库,基本达到了万能了吧。

总结

项目地址:AVFFmpegLib

这里就不在介绍如何编译了,感兴趣的可以看 mobile-ffmpeg 项目,我这里也是基于它进行二次封装开发。

参考

  • EpMedia

  • FFmpeg 常用命令

  • mobile-ffmpeg

  • Android 音视频编辑经验总结及开源工程分享


技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

推荐阅读:

音视频面试基础题

OpenGL ES 学习资源分享

开通专辑 | 细数那些年写过的技术文章专辑

NDK 学习进阶免费视频来了

推荐几个堪称教科书级别的 Android 音视频入门项目

觉得不错,点个在看呗~

相关文章:

  • 花里胡哨的3D翻页卡片,隔壁产品都馋哭了
  • 从Chrome小恐龙游戏学习2D游戏制作
  • UML科普文,一篇文章掌握14种UML图
  • 黑白键上的字节跳动:全球最大钢琴MIDI数据集背后的故事
  • 当当福利,音视频开发囤书活动!
  • 推荐我录制的免费 Android NDK 进阶视频
  • 炫酷的Android时钟UI控件,隔壁产品都馋哭了
  • 面试官:如何监测应用的 FPS ?
  • 一张图概括淘宝直播背后的前端技术 | 赠送多媒体前端手册
  • 活用 Shader,让你的页面更小,更炫,更快
  • 再见!onActivityResult!你好,Activity Results API!
  • 10 个你可能还不知道 VS Code 使用技巧
  • 细数 2020 年官方对 Android 的那些重大更新!
  • 64位系统究竟牛逼在哪里?
  • 如何区分IO密集型、CPU密集型任务?
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • css属性的继承、初识值、计算值、当前值、应用值
  • Django 博客开发教程 8 - 博客文章详情页
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • ES6 学习笔记(一)let,const和解构赋值
  • gitlab-ci配置详解(一)
  • Hexo+码云+git快速搭建免费的静态Blog
  • Node项目之评分系统(二)- 数据库设计
  • opencv python Meanshift 和 Camshift
  • php ci框架整合银盛支付
  • Python利用正则抓取网页内容保存到本地
  • zookeeper系列(七)实战分布式命名服务
  • 大型网站性能监测、分析与优化常见问题QA
  • 机器学习学习笔记一
  • 基于Android乐音识别(2)
  • ------- 计算机网络基础
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 设计模式(12)迭代器模式(讲解+应用)
  • 学习JavaScript数据结构与算法 — 树
  • 优化 Vue 项目编译文件大小
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • 【云吞铺子】性能抖动剖析(二)
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • MyCAT水平分库
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • #13 yum、编译安装与sed命令的使用
  • #ifdef 的技巧用法
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (02)vite环境变量配置
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (万字长文)Spring的核心知识尽揽其中
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .equals()到底是什么意思?
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .net 程序发生了一个不可捕获的异常