这里的播放器演示程序用于播放一个本地文件,因而不需要关心播放网络上的媒体数据时的网络传输问题。

对于播放本地媒体文件的播放器来说,所要完成的工作主要包括:解封装 -> 音频解码/视频解码 -> 对于音频来说,还需要做格式转换和重采样 -> 音频渲染和视频渲染。本文的代码位于 GitHub ffmpeg-mediaplayer-android。

音频渲染

音频播放通过 Android 的 OpenSL ES 接口实现。具体的代码基于 ndk-samples 中的 audio-echo 修改获得。

ndk-samples 中的 audio-echo 在播放或录制的缓冲区 overflow 时,会自动停止播放或录制,demo 中针对此问题做了一些修改。此外,修改 audio-echo 的代码以支持定制播放的音频内容。

为 Android 应用集成 ffmpeg 库

通过 mobile-ffmpeg 编译获得 ffmpeg 的几个库文件,这个工程所基于的 ffmpeg 版本还算比较新,为 FFmpeg 4.4 的。在 Mac 平台,要为 Android 平台编译 ffmpeg 的动态链接库,首先需要安装 Android 的 SDK 和 NDK 包。随后,为 android 平台编译动态链接库的方法为:

% export ANDROID_NDK_ROOT=/Users/henryhan/bin/android-ndk-r21e
% export ANDROID_HOME=/Users/henryhan/Library/Android/sdk
% ./android.sh

编译方法基本上就是设置环境变量 ANDROID_NDK_ROOTANDROID_HOME 分别只想 NDK 和 SDK 的路径,然后执行 mobile-ffmpeg 提供的脚本文件 android.shandroid.sh 脚本会完成所有的编译动作。

android.sh 脚本的执行过程大致为:mobile-ffmpeg/android.sh -> mobile-ffmpeg/build/main-android.sh -> mobile-ffmpeg/build/android-ffmpeg.shmobile-ffmpeg/build/android-ffmpeg.sh 脚本中可以看到比较详细的编译配置。编译过程中,编译日志都会输出到 mobile-ffmpeg/build.log,从这个文件中可以看到比较详细的编译过程的信息。

mobile-ffmpeg/build/android-ffmpeg.sh 脚本中可以看到:

LIB_NAME="ffmpeg"
. . . . . .
./configure \--cross-prefix="${BUILD_HOST}-" \--sysroot="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot" \--prefix="${BASEDIR}/prebuilt/android-$(get_target_build)/${LIB_NAME}" \
. . . . . .
rm -rf ${BASEDIR}/prebuilt/android-$(get_target_build)/${LIB_NAME}
make install 1>>${BASEDIR}/build.log 2>&1

因而编译出来的头文件和动态链接库文件都将被安装到 mobile-ffmpeg/prebuilt/android-$(get_target_build)/ffmpeg,如为 x86_64 编译出来的动态链接库被安装在了 mobile-ffmpeg/prebuilt/android-x86_64/ffmpeg/lib 目录下,x86_64 平台的头文件被放在了 mobile-ffmpeg/prebuilt/android-x86_64/ffmpeg/include 目录下,而为 arm64 编译出来的动态链接库被安装在了 mobile-ffmpeg/prebuilt/android-arm64/ffmpeg/lib 目录下,arm64 平台的头文件被放在了 mobile-ffmpeg/prebuilt/android-arm64/ffmpeg/include 目录下,其它 ABI 依此类推。

mobile-ffmpeg 的 android.sh 还会编译生成 ffmpeg 库的 AAR 文件,位于 mobile-ffmpeg/prebuilt/android-aar/mobile-ffmpeg/mobile-ffmpeg.aar

可以参考文章 Android studio中NDK开发(二)——使用CMake引入第三方so库及头文件 和 NDK–CMakeLists配置第三方so库 在 CMake 中添加预编译的库,大体方法为:

  • 将 ffmpeg 库的几个动态链接库文件和头文件拷贝到适当的位置。
  • 为 CMake 添加库。当需要添加多个 so 库依赖时,也需要为 CMake 添加多个 add_library() 项。
  • 设置库二进制文件的搜索路径。需要添加多个 so 库依赖时,也需要为 CMake 添加多个 set_target_properties() 项。
  • 设置头文件搜索路径。为 JNI 的 target 添加头文件搜索路径,添加一个 target_include_directories(),但其中可以包含一个或多个路径。
  • 设置链接依赖。为 JNI 的 target 添加库依赖,每个库一个 target_link_libraries() 的项。

在通过 CMake 的自动打包预构建依赖项时,不再需要在 build.gradle 中像下面这样设置预构建项的地址:

    sourceSets {main {jniLibs.srcDirs = ["src/main/libs"]}}

有了 Android Gradle 插件 4.0,上述配置不再是必需的,并且会导致构建失败:

-----------
* What went wrong:
Execution failed for task ':app:mergeReleaseNativeLibs'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction> 2 files found with path 'lib/x86/libswscale.so' from inputs:- /Users/henryhan/Projects/Shopee/henry-entrytask/app/build/intermediates/merged_jni_libs/release/out- /Users/henryhan/Projects/Shopee/henry-entrytask/app/build/intermediates/cmake/release/objIf you are using jniLibs and CMake IMPORTED targets, seehttps://developer.android.com/r/tools/jniLibs-vs-imported-targets

更多信息,可以参考 Google 的官方文档。

通过 System.loadLibrary() 加载 ffmpeg 和 JNI 的 so 文件时,需要注意加载 so 的顺序,被依赖的 so 文件需要先加载,依赖的 so 需要后加载,如 swscale 库依赖 avutil 库,则需要 System.loadLibrary("avutil"); 放在 System.loadLibrary("swscale"); 的前面,否则会报错说符号找不到。

ffmpeg 是一个纯 C 的库,就像在任何 C++ 代码中使用 FFmpeg 的 API 那样,如果 JNI 代码用 C++ 写,ffmpeg 头文件的 include 要放在 extern "C" 里。

音视频解封装及解码

代码参考了 HelloFFmpeg 和 goffmpeg 这些工程的代码。

视频:解封装获得 H264 编码帧 -> 解码获得裸 YUV 视频流。
音频:解封装获得 AAC 编码音频帧 -> 解码获得裸 PCM 音频流 -> 经过样本格式转换(样本格式由 32 位 float 型转为 16 为有符号整型),重采样(采样率由 48 kHz 转为 44.1 kHz),和通道数转换(从立体声双通道转为单通道)获得适合于送给 Android 平台播放的裸 PCM 音频流。

解码音频直接获得的是所谓 Non-interleaved 格式的裸 PCM 音频流,即内存中保存音频数据的方式为 LLLL…RRRR…,先是所有的左声道的数据,后是右声道的数据。这种格式保存的数据易于做数据处理,如编解码等,一般情况下音频数据处理都是针对某一个声道的连续数据的。但这种格式保存的数据对于播放和传输等场景不是很友好,这会要求播放设备或者是数据的接收端拿到所有数据才能开始播放。因而播放设备一般请求的裸 PCM 音频格式为所谓的 interleaved 的,即以 LRLRLRLR… 这种方式保存的数据。

在 ffmpeg 的术语和概念体系中,用 AVSampleFormat 的 planar 来表示这种音频数据保存格式的差异。对于 planar 的音频 PCM 数据保存格式,其保存音频数据的方式为 LLLL…RRRR…,如 AV_SAMPLE_FMT_S16P,AV_SAMPLE_FMT_FLTP 这些格式,而非 planar 的格式,其保存音频数据的方式则为 LRLRLRLR… 这种。

对于音频重采样,需要注意保持输入样本格式与实际数据格式的一致性,如解码出来的格式为 AV_SAMPLE_FMT_FLTP,但为了后面的处理方便,将解码出来的音频数据做了 interleave,将音频样本数据格式转为了 AV_SAMPLE_FMT_FLT,则也应该通过 av_opt_set_sample_fmt(swr_ctx_, "in_sample_fmt", format, 0); 将输入样本格式设置为 AV_SAMPLE_FMT_FLT。ffmpeg 提供了一个 API enum AVSampleFormat av_get_alt_sample_fmt(enum AVSampleFormat sample_fmt, int planar); 来做这种格式的转换。

整个解封装和解码过程由播放线程驱动。播放线程中,请求播放数据的回调上来的时候,检查缓冲区里是否有一定量的解码音视频数据,当数据不足时,通过 demuxer 和 decoder 解封装及解码音视频数据,并保存在缓冲区中。

解码和解封装过程的测试,可以将解码出来的数据直接保存在文件中,然后通过 ffplay 之类的工具播放检查。

采用 ffplay 查看YUV数据包括视频或者图片:

$ ffplay -f rawvideo -video_size 854x480   trailer.yuv

注:
(1)-f rawvideo:这个选项可加可不加。
(2)YUV 文件不包涵宽高数据,所以必须用 -video_size 指定宽和高,格式为:widthxheight
(3)test.yuv可以是一帧(图片)或者多帧(视频)数据

裸 PCM 数据播放命令如下:

$ ffplay -f f32le -ac 2 -ar 48000 trailer_.pcm
$ ffplay -f s16le -ac 1 -ar 44100 trailer_441.pcm

上面的命令用于播放样本格式为 32 位 float,采样率为 48 kHz,通道数为 2 的裸 PCM 音频流。下面的命令用于播放样本格式为 16 位有符号整型,采样率为 44.1 kHz,通道数为 1 的裸 PCM 音频流

ffmpeg -i trailer.mp4  -ar 44100 -ac 1 output_1.mp4

参考了如下文档:
利用av_read_frame解码h264、mp4多媒体文件为yuv
ffmpeg 查看YUV图片/视频
FFmpeg解码MP4文件为h264和YUV文件
FFmpeg —— 9.示例程序(三):音视频分离(分离为PCM、YUV格式)
FFmpeg 重采样示例代码

音视频播放

视频渲染参考了 Android 基于FFmpeg的视频播放渲染 CMake + ANativeWindow,也参考了其代码 NDKtest,这个 demo 在当前的开发环境下基本上没法跑,这个 demo 的 gradle 版本过老,同时它包的 ffmpeg so 只有 armeabi ABI 的,但代码还是有一定参考价值的。不过代码里有非常多的资源泄漏,既有 android 的资源泄漏,如 native window handle,也有 ffmpeg 的对象的泄漏,如 AVFrame 和 SwsContext 等。

如前所述,解封装和解码过程由音频播放线程驱动,音频播放线程会把解码后的音视频数据放进缓冲区中。应用中会启动专门的视频播放线程,视频播放线程定时向视频缓冲区请求 YUV 视频数据,如果视频 YUV 数据存在就拿去播放。

播放控制及音视频同步

Android 调用系统文件选择器并拿到文件路径的方法参考了 Android 选择文件并返回路径 及其代码 ForeverLibrary 。

对于拖拽的实现,用户在拖拽进度条时,在一个全局状态中设置了进度值,在音频播放线程中请求音频数据的回调中检查进度值,并根据进度值,通过 ffmpeg 的 API 实现 seek。ffmpeg 的 seek API 用法参考了 How to seek in FFmpeg C/C++ 等。

对于暂停的实现,用户在点击暂停键时,同样在全局状态中设置暂停状态,在音频播放线程中请求音频数据的回调中检查暂停状态,并采取一定的措施。

测试文件下载

1、地址:http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4 1分钟
2、地址:http://vjs.zencdn.net/v/oceans.mp4
3、地址:https://media.w3.org/2010/05/sintel/trailer.mp4 52秒
4、地址:http://mirror.aarnet.edu.au/pub/TED-talks/911Mothers_2010W-480p.mp4 10分钟

其他各种格式,MP4, flv, mkv, 3gp 视频下载
https://www.sample-videos.com/index.php#sample-mp4-video

MPlayer 官方提供的各种视频格式的测试文件,载地址:http://samples.mplayerhq.hu/

测试视频的下载地址
http://ultravideo.cs.tut.fi/#testsequences
http://www.tanimoto.nuee.nagoya-u.ac.jp/~fukushima/mpegftv/
http://www.tanimoto.nuee.nagoya-u.ac.jp/~fukushima/mpegftv/Akko.htm

测试视频的下载地址
测试视频,音频,图片下载
测试用视频下载
测试用在线视频地址
常用各种视频测试文件
sample-videos

开发过程中的问题分析调查

解封装出来的 H264 和 AAC 文件,由于 H264 有编码帧的分割字节串,AAC 有 ADTS header,转储的这些文件可以直接用一些播放器播放,如 VLC player。
对于解码出来的 YUV 和 PCM 数据,ffplay 可以用于播放裸 YUV 和 PCM 的音视频流,但需要正确指定裸流的参数。Audacity 可以用于播放裸的 PCM 音频流并查看音频信号的波形。

我们在开发调试时,也可以通过命令行方式显示的给某一个包授予权限,如下所示
adb shell pm grant com.xxx.xxx android.permission.READ_EXTERNAL_STORAGE

基于 FFmpeg 的播放器 demo相关推荐

  1. 基于ffmpeg的播放器,播放m3u8文件时,seek问题

    文章目录 1,准备知识, seek代码流程: 策略: 2,问题描述, 3,原因分析及其修改, 拿到这个问题,在不debug代码或查看日志的情况下,根据上面1的准备知识,可以大概判断出原因. 针对问题视 ...

  2. 基于ffmpeg网络播放器的教程与总结

    一.         概述 为了解决在线无广告播放youku网上的视频.(youku把每个视频切换成若干个小视频). 视频资源解析可以从www.flvcd.com获取,此网站根据你输入的优酷的播放网页 ...

  3. KXMovie基于ffmpeg的播放器

    之前研究了一段时间的媒体播放器,现在项目发布了,时间比较多一点,所以想为开源界贡献微薄之力 修改了一点KXMovie的东西,贡献给大家,欢迎大家在github上收听我 : )   https://gi ...

  4. IOS 基于ffmpeg VR播放器

    使用场景 支持rtsp,rtmp等的实时流播放的全景播放器 开源的全景播放器HTY360Player有一个问题就是用的是系统的播放器,所以无法支持rtsp或rtmp协议,所以解码部分使用Kxmovie ...

  5. 【Android-Service】基于MVP的音乐播放器demo实现思路(附源码)

    最近在学习service相关的内容,在该部分的学习过程中,根据学习视频中的内容进行了总结归纳,以下是音乐播放器demo的开发思路,具体步骤及源码: 有关MVP框架的内容可看: link. 实现效果: ...

  6. VLC播放器Demo(录像,截图等功能),Android播放器Demo可二次开发。ffmpeg-Kit (录像,截图,合流播放,合流推送,等一些列视频操作功能),可二次开发。

    VLC播放器Demo(录像,截图等功能),可二次开发. ffmpeg-Kit (录像,截图,合流播放,合流推送,等一些列视频操作功能),可二次开发. 如果帮助的到了您,请您不要吝啬你的Star,先谢谢 ...

  7. 基于android音乐播放器的设计

    本科毕业论文(设计)诚信声明 本人郑重声明:所呈交的毕业论文(设计),题目<---基于android音乐播放器的设计----------->是本人在指导教师的指导下,进行研究工作所取得的成 ...

  8. 最简单的基于FFmpeg的推流器(以推送RTMP为例)

    ===================================================== 最简单的基于FFmpeg的推流器系列文章列表: <最简单的基于FFmpeg的推流器(以 ...

  9. FFmpeg音频播放器(8)-创建FFmpeg播放器

    原文地址::https://www.jianshu.com/p/73b0a0a9bb0d 相关文章 1.FFmpeg音频解码播放----https://www.jianshu.com/p/76562a ...

最新文章

  1. Emscripten 单词_初一(上)掌握这 4 大类发音规律,英语记单词很轻松
  2. ID vs Class 老生常谈的选择器问题
  3. GitHub 发布了一款重量级产品,可直接运行代码!
  4. 你面对以希望为名的绝望微笑
  5. 日志文件在VS中输出为乱码问题
  6. 领域模型(domain model)贫血模型(anaemic domain model)充血模型(rich domain model)
  7. 无线网服务器mac是什么,电脑MAC和LAN MAC以及WIRELESS MAC是什么关系?
  8. 关于java的外语文献_java英文参考文献(涵盖3年最新120个)
  9. 基于OpenCV视频帧差分的身高检测
  10. HTML注册表单的页面制作
  11. 思岚SLAMTEC A1开箱测试(实现雷达数据的查看+hector_slam建图)
  12. 【仪器常用操作方法】33500B函数发生器常用操作方法
  13. 20余年互联网沉浮史:剩者为王
  14. 一个人赶着鸭子去每个村庄卖,每经过一个 村子卖去所赶鸭子的一半又一只。 这样他经过了 七个村子后还剩 两只鸭子,问问他出发时共赶多少只鸭子?经过每个村子卖出多少只鸭子?
  15. 【学习笔记】Windows格式文档转换成Unix格式
  16. 阿里淘系优质开源项目推荐(下)
  17. 程序化交易策略系统的构成
  18. html音频base64编码,录音文件与Base64编码相互转换的方法
  19. 几种光学毫米波产生方式的优缺点
  20. 企业如何正确挑选源代码加密软件

热门文章

  1. 组合模式coding
  2. 树和二叉树的基本概念(二级)
  3. hough变换检测直线 matlab,求能够运行的用matlab进行hough变换直线检测的程序。急!...
  4. Servlet的第一个程序HelloWorld
  5. docker-machine create -d generic 运行的波折过程及遇见的问题
  6. Apache—DBUtils框架开发学习实例
  7. Model层视频播放关闭问题及手机视频播放的适配问题解决方案
  8. 来和小伙伴一起学习响应式网页设计吧
  9. [置顶]       ibatis做分页
  10. .NET服务器应用程序开发