Android音视频

了解音视频

1,音频

声音将模拟信号转为数字信号,要经过三个步骤:采样、量化、编码

人耳能够听到的声音的频率范围是20Hz~20KHz。根据奈奎斯特采样原理,按比声音最高频率高2倍以上的频率对声音进行采样,所以采样率一般是44100Hz(略大于20KHz x 2),即1秒采用44100次。

采样后的数据用二进制信号来表示,一般有8比特、16比特和32比特等。

音频的裸数据格式是脉冲编码调制数据(PCM:Pulse Code Modulation)
描述pcm需要量化格式、采样率以及声道数
我们以常见的值为例来算下对应的比特率和存储空间大小。
量化格式为16比特(2个字节)、采样率为44100HZ、声道数为2,
则对应的比特率= 2x44100x2 x8/1024= 1378kbps
如果是4分钟的音频,对应的文件大小为1378x60x4/8/1024 = 40MB

这个还是比较大的,为了减少存储空间和传输的流量,需要进行压缩编码,常见压缩编码有mp3、aac、wav等编码方式。其中wav是在pcm的基础上添加44个字节的头,属于无损压缩、而mp3和aac属于有损压缩,特别是aac压缩率更大些,一般用于低于128Kbit/s以下的音频编码。

2,视频

封装格式

对于数字媒体数据来说,容器就是一个可以将多媒体数据混在一起存放的东西,就像是一个包装箱,它可以对音、视频数据进行打包装箱,将原来的两块独立的媒体数据整合到一起,当然也可以单单只存放一种类型的媒体数据。

有时候,多媒体容器也称封装格式,它只是为编码后的多媒体数据提供了一个 “外壳”,也就是将所有的处理好的音频、视频或字幕都包装到一个文件容器内呈现给观众,这个包装的过程就叫封装。常用的封装格式有:MP4,MOV,TS,FLV,MKV 等。

319fe86d9e589ae5e673957a54a6724f.png

三种帧的说明

I 帧:帧内编码帧,帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)

P帧:前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要之前缓存的画面叠 加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)

B帧:双向预测内插编码帧。B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别,换言之,要解码B帧。不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画 面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU会比较累。

I、B、P帧是根据压缩算法的需要,是人为定义的,他们都是实实在在的物理帧。 一般来说,I帧的压缩率是7(跟JPG差不多), P帧是20,B帧可以达到50.可见使用B帧能节省大量空间, 节省出来的空间可以用来保存多一些帧,这样在相同码率下,可以提供更好的画质

压缩算法的说明

h264的压缩方法:

  1. 分组:把几帧图像分为一组(GOP,也就是一个序列),为防止运动变化,帧数不宜取多
  2. 定义帧:将每组内各帧图像定义为三种类型,即 I 帧、B帧和P帧
  3. 预测帧:以帧I作为基础帧,以帧预测P帧,再由 I 帧和P帧预测B帧 4、数据传输:最后将 I 帧数据与预测的差值信息进行存储和传输
  4. 数据传输:最后将 I 帧数据与预测的差值信息进行存储和传输

帧内(Intraframe)压缩也称为空间压缩(Spatial compression)。当压缩一帧图像时,仅考虑本帧的数据而不考 虑相邻帧之间的冗余信息,这实际上与静态图像压缩类似。帧内一般采用有损压缩算法,由于帧内压缩是编码一个完 整的图像,因此可以独立的解码、显示。帧内压缩一般达不到很高的压缩,跟编码jpeg差不多。

帧间(Interframe)压缩的原理是:相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点,也即连 续的视频及其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减少压 缩比。帧间压缩也称为时间压缩,它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩一般是无损的。帧差值 (Frame differencing)算法是一种典型的时间压缩发,它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻 帧的差值,这样可以大大减少数据量。

FFmpeg

FFmpeg 是一款知名的开源音视频处理软件,它提供了丰富而友好的接口支持开发者进行二次开发。

FFmpeg 读作 “ef ef em peg” ,其中的 “FF” 指的是 “Fast Forward”,“mpeg” 则是 “Moving Picture Experts Group” (动态图像专家组)。

FFmpeg 项目功能复杂而庞大,基本上支持所有常见的音视频处理操作,如封装格式转换、音视频转码、音视频播放和剪辑、视频添加水印滤镜等。

尽管 FFmpeg 功能强大,但是由于其采用的是带有传染性的 LGPL/GPL 开源协议,所以一些大厂基本上都是自己独立开发类似的音视频处理库,甚至在接口和组织模块上模仿 FFmpeg 。

因此,学习 FFmpeg 不仅能够帮助你掌握音视频开发的相关知识脉络,还能让你快速适应不同的音视频处理框架。

1,下载源码

进入官网下载 Download FFmpeg

2,准备环境

首先需要安装sdl2,否则编译完之后没有ffplay

sudo apt-get install libsdl2-2.0
sudo apt-get install libsdl2-dev

在解压完的FFmpeg目录下打开终端

 

配置参数

./configure --prefix=/usr/local/ffmpeg --enable-debug=3 --enable-shared --disable-static --enable-sdl2

期间可能会出现各种工具不存在的问题,按照提示安装即可

3,编译安装

编译

make -j4

安装

sudo make install

编辑环境变量

gedit ~/.bashrc
export PATH="/usr/local/ffmpeg/bin:$PATH"
source ~/.bashrc

验证,输入ffmpeg -version 提示如下界面即表示成功

ffmpeg -version

4,遇到的问题

问题1: 

ffmpeg: error while loading shared libraries: libavdevice.so.59: cannot open shared object file: No such file or directory

按照如下步骤设置下路径即可

#寻找文件在哪
find /usr -name 'libavdevice.so.59'

#export
export LD_LIBRARY_PATH=/usr/local/ffmpeg/lib/

5,使用FFmpeg处理音视频文件

一、主要参数

◼ -i 设定输入流
◼ -f 设定输出格式(format)
◼ -ss 开始时间
◼ -t 时间长度

二、音频参数:

◼ -aframes 设置要输出的音频帧数
◼ -b:a 音频码率
◼ -ar 设定采样率
◼ -ac 设定声音的Channel数
◼ -acodec 设定声音编解码器,如果用copy表示原始编解码数据必须被拷贝。
◼ -an 不处理音频
◼ -af 音频过滤器

三、视频参数:

◼ -vframes 设置要输出的视频帧数
◼ -b 设定视频码率
◼ -b:v 视频码率
◼ -r 设定帧速率
◼ -s 设定画面的宽与高
◼ -vn 不处理视频
◼ -aspect aspect 设置横纵比 4:3 16:9 或 1.3333 1.7777
◼ -vcodec 设定视频编解码器,如果用copy表示原始编解码数据必须被拷贝。
◼ -vf 视频过滤器

实例:

1,转换视频文件到不同的格式

ffmpeg -i slam_dunk.mp4 slam_dunk.mpeg

 2,设置视频的屏幕高宽比

ffmpeg -i slam_dunk.mp4 -aspect 5:4 output.mp4

 通过以上命令可以实现 视频剪辑,修改视频分辨率,媒体文件移除音频流,移除视频流等操作

FFmpeg在Android上的应用

1,使用NDK编译So文件

NDK下载地址 不受支持的 NDK 下载  |  Android NDK  |  Android Developers

下载完成之后解压到目录。

在ffmpeg目录新增一个脚本文件 build_android_clang_zjld_64.sh

编写编译脚本 

#!/bin/bash

#这里配置先你的 NDK 路径
export NDK=/home/enwali/MyWork/MyFfmpeg/NDK/ndk-r21e/android-ndk-r21e
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64

function build_android
{

./configure \
--prefix=$PREFIX \
--enable-neon  \
--enable-hwaccels  \
--enable-gpl   \
--disable-postproc \
--disable-debug \
--enable-small \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-static \
--enable-shared \
--disable-doc \
--enable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--disable-symver \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS"

make clean
make -j16
make install

echo "============================ build android arm64-v8a success =========================="

}

#arm64-v8a
ARCH=arm64
CPU=armv8-a
API=21
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"

build_android

编写完成之后打开终端,运行

./build_android_clang_zjld_64.sh

 运行完之后会在当前目录生成Andoird文件夹。在对应的目录下生成六个so文件

六个功能模块:

libavformat:封装模块,多媒体文件或协议的封装和解封装库,如 Mp4、Flv 等文件封装格式,RTMP、   RTSP 等网络协议封装格式;
libavcodec:音视频编解码库;
libavfilter:音视频、字幕滤镜库;
libswscale:图像格式转换库,如YUV转RGB等;
libswresample:音频重采样库,例如操作音频采样,音频通道布局转换,布局调整;
libavutil:工具库;

 如果需要编译32位so,需要修改编译脚本

#armv7-a
ARCH=arm
CPU=armv7-a
API=21
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "

2,Android 项目集成FFmpeg

将编译好的so文件以及头文件引入到项目

cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

set(jnilibs ${CMAKE_SOURCE_DIR}/../jniLibs)
set(libname learn-ffmpeg)

include_directories(include ${CMAKE_SOURCE_DIR}/util)

link_directories(${jnilibs}/${ANDROID_ABI})

file(GLOB src-files ${CMAKE_SOURCE_DIR}/*.cpp)

add_library(${libname} SHARED ${src-files} )

set(third-party-libs avformat avcodec avfilter swresample swscale avutil )

set(native-libs android EGL GLESv3 OpenSLES log m z )

target_link_libraries( ${libname} ${log-lib} ${third-party-libs} ${native-libs} )

3,视频播放处理步骤

1,初始化网络 让ffmpeg能够使用网络,创建封装格式上下文

avformat_network_init();    

2,创建音视频封装格式上下文(代表一个 视频/音频 包含了视频、音频的各种信息)

formatContext = avformat_alloc_context();

3,打开媒体地址(文件地址、直播地址),解封装

int ret = avformat_open_input(&formatContext, url, nullptr, &opts);

4,获取媒体中的 音视频流 (给 formatContext里的 streams等成员赋值)

if (avformat_find_stream_info(formatContext, NULL) < 0) {
    LOGE("2. 查找流失败:%s", av_err2str(ret));
    if (javaCallHelper) {
        javaCallHelper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_FIND_STREAMS);
    }
    return;
}

5,通过 当前流 使用的 编码方式,查找解码器

AVCodec *dec = avcodec_find_decoder(pCodecParameters->codec_id);

6,打开解码器

if (avcodec_open2(codecContext, dec, 0) != 0) {
   LOGE("3.4 打开解码器失败:%s", av_err2str(ret));
   if (javaCallHelper)
      javaCallHelper->onError(THREAD_CHILD, FFMPEG_OPEN_DECODER_FAIL);
   return;
}

7,发送数据到解码器

ret = avcodec_send_packet(avCodecContext, packet);

8,从解码器获取已经解码的数据

ret = avcodec_receive_frame(avCodecContext, frame);

9,绘制图像到surfaceView

每一种操作系统都定义了自己的窗口系统,而 ANativeWindow 就是 Android 的本地窗口,在 Android Java 层,Surface 又继承于 ANativeWindow ,实际上 Surface 是 ANativeWindow 的具体实现,所以一个 ANativeWindow 表示的就是一块屏幕缓冲区。

我们要渲染一帧图像,只需要将图像数据刷进 ANativeWindow 所表示的屏幕缓冲区即可。

ANativeWindow_lock(window, &window_buffer, 0)
memcpy(dst_data + i * dst_linesize, src_data + i * src_linesize, dst_linesize);
ANativeWindow_unlockAndPost(window);

其余详情结合Demo讲解

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值