解决方案:Android端直播SDK实现方案
优采云 发布时间: 2022-12-02 11:36解决方案:Android端直播SDK实现方案
概述
直播系统的架构一般分为采集模块、预览模块、处理模块、编码模块、推流模块。
将这五个模块串联起来,就构成了整个直播系统的数据流。如下所示:
音频采集:采集原创
PCM 数据。
音频处理:对音频进行混音消除、降噪、自动增益等处理。
音频编码:将PCM格式的数据编码为AAC格式。
视频捕捉:*敏*感*词*/屏幕流的捕捉;YUV 格式或纹理格式。
视频处理:对视频进行美颜/滤镜等处理。
预览:将处理后的视频流渲染并显示在屏幕上。
视频编码:将texture或YUV格式的原创
视频流压缩成H264格式。
推流:将AAC格式的音频流和H264格式的视频流打包推送为flv格式。
模块化设计
为了充分利用CPU的多核,提高流媒体系统的效率,低延迟,模块的设计采用了多线程模型。线程之间的交互是通过阻塞队列实现的。由于队列需要被多个线程操作,所以需要保证队列的安全性。为了保证队列的安全,在放入队列之前先给队列加锁,然后再对队列进行操作。操作队列结束后,发出信号指令,告诉block所在的线程继续操作队列。添加队列后的模块图如下图所示:
音频捕捉
Android端音频采集常用的方案有3种:AudioRecord、OpenSL、AAudio。
AudioRecord是Android上层的一个API,通过它可以采集PCM格式的音频数据。
OpenSL是Native层提供的API,可以采集或播放PCM。
详情链接:
AAudio是Native层提供的API,是Android O引入的新的音频API。
详情链接:
音频采集在独立线程中执行。将采集到的音频放入pcm队列。这里我们以AudioRecord为例介绍一下AudioRecord采集音频的过程:
配置参数(采样率、通道数、采样格式)并获取AudioRecord采集音频缓冲区的大小
public static int SAMPLE_RATE_IN_HZ = 44100;
private final static int CHANNEL_CONFIGURATION = AudioFormat.CHANNEL_IN_MONO;
private final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
<p>
" />
int bufferSizeInBytes = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ, CHANNEL_CONFIGURATION, AUDIO_FORMAT)
</p>
创建录音
AudioRecord audioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE_IN_HZ, CHANNEL_CONFIGURATION, AUDIO_FORMAT, bufferSizeInBytes);
配置AudioRecord后,需要查看AudioRecord的状态。可能因为权限问题或者其他原因导致AudioRecord创建失败或者状态不正确;
if (audioRecord == null || audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
throw new AudioConfigurationException();
}
开始采集
AudioRecord创建成功后,就可以根据startRecording()接口开始采集视频了。
获取音频数据
获取音频数据需要一个线程不断地读出AudioRecord缓冲区中的音频数据。
int size = audioRecord.read(audioSamplesBuffer, 0, audioSamplesBufferMaxSize);
将音频数据放入PCM队列
pcm队列的设计后面会介绍
停止采集
和释放资源
停止音频采集线程,调用stop停止采集,调用release释放AudioRecord创建的资源。
音频编码
音频编码格式有很多,mp3、aac、wma、ogg、pous、amr等,aac在低码率场景下音质非常好,aac编码在移动平台上应用最广泛,无论是aac单独的音频编码或视频中的音频流部分。
音频编*敏*感*词*:fdk_aac、MediaCodec
音频编码线程从pcm队列中获取数据编码,编码完成后将AAC数据放入AAC队列中。
这里引入软码libfdk_aac,将pcm编码成aac流。这里介绍的libfdk_aac是基于ffmpeg的。如果以后要使用其他编码库,可以通过ffmpeg配置。
基于ffmpeg的API编码aac的好处是只需要写一段音频编码代码即可。对于不同的编码器,后面只需要调整对应的编码器ID或者编码器名称,就可以对不同的格式进行编码。音频文件。由于要使用第三方库libfdk_aac对aac文件进行编码,所以在交叉编译ffmpeg时必须先编译libfdk_aac库。音频编码是通过调用ffmpeg的API来实现的。
音频编码需要在线程中实现,线程会从pcm队列中获取pcm数据。当队列为空时,等待当前线程。当队列中有数据时,信号唤醒当前线程。得到pcm音频数据后,开始编码。编码完成后,将编码后的aac数据放入aac队列。
OpenGL上下文环境搭建后台
" />
OpenGL 不负责窗口管理和上下文管理。OpenGL 窗口管理和上下文管理将由各个平台或设备完成。为了在 openGL 的输出和设备的屏幕之间建立桥梁,需要使用 EGL。EGL是双缓冲工作模式,即有一个Back Frame Buffer和一个Front Frame Buffer。正常绘图操作的目标是 Back Frame Buffer。操作完成后,调用eglSwapBuffer api将绘制的Back Frame Buffer交换到Front Frame Buffer并显示。在Android平台上,使用了EGL的机制。EGL承担为OpenGL提供上下文环境和窗口管理的责任。
安卓上下文
在Android平台上使用opengl es,第一种方式是直接使用GLSurfaceView。这种方式使用OpenGL ES比较简单,因为不需要开发者搭建OpenGL ES的上下文环境,也不需要为OpenGL ES创建显示设备。但是任何事情都有两个方面,有优点也有缺点。使用 GLSurfaceView 不够灵活。OpenGL ES的很多核心用法,比如共享上层上下文实现多线程共同操作一个纹理等,是不能用的。所以这里需要通过EGL api搭建OpenGL上下文环境,基于C++环境搭建。因为如果用Java层来构建,对于普通应用来说可能是可行的,但是对于视频应用来说,为了效率和性能,
任何 opengl 命令都必须在其自己的 OpenGL 上下文中运行,因此在 EGL 构建 opengl 上下文后,它可以执行 OpenGL ES。
OpenGL处理模块需要在线程中实现,线程函数:
EGL 环境管理 渲染窗口管理 纹理创建 视频流处理 视频捕获和预览
Camera的参数配置这里就不介绍了,这里只介绍Camera如何结合OpenGL实现预览。
SurfaceView的转换
首先UI层构造一个SurfaceView通过SurfaceView获取Surface,然后在NDK中将Surface转化为ANativeWindow,在EGL中通过ANativeWindow创建EGLSurface。
创建外部纹理
通过 OpenGL 创建外部纹理
将外部纹理设置为 Camera
通过2创建的外部纹理ID创建一个SurfaceTexture,并设置一个*敏*感*词*器SurfaceTexture.setOnFrameAvailableListener(); 每次将视频流渲染到 SurfaceTexture 时都会回调此方法。通过 setPreviewTexture 接口将 SurfaceTexture 设置为 Camera。
OpenGL操作外部纹理实现预览
Camera/Camera2采集*敏*感*词*流并将采集到的*敏*感*词*流渲染成外部纹理,然后通过OpenGL将外部纹理渲染到窗口,实现预览。
视频采集预览流程图如下:
视频编码
MediaCodec的配置和使用这里就不介绍了。这里只介绍OpenGL ES如何与MediaCodec进行交互。
MediaCodec的输入视频流经过OpenGL模块处理,通过createInputSurface() api得到一个Surface,最后Surface创建一个EGL Surface。
OpenGL通过渲染外部纹理到MediaCodec inputsurface通知MediaCodec获取编码数据,获取编码数据后,通过处理进入h264队列。
MediaCodec与OpenGL运行图:
推流:
推流模块按照特定的顺序从aac队列/h264队列中获取数据,进行处理,然后封装成flv格式进行推流。
流媒体模块通过FFmpeg中集成的rtmp协议实现流媒体。这样我们就可以通过ffmpeg的写入文件的API来实现流式传输了。
这样做的好处是你只需要编写一个streaming模块的代码。当我们要在本地写音视频流时,streaming模块的代码不需要修改,只需要修改我们传给streaming模块的路径即可。例如:mnt/sdcard/0/test.mp4,可以将视频流以mp4格式保存到本地。
解决方案:WordPress程序搭建的网站如何实现调用其他网站文章功能呢?
作为一个新闻门户网站的站长,最难做的就是采集
更全面的内容。一般在这种情况下,人们会从较大的网站上采集
文章,发布在自己的网站上。那么使用WordPress程序搭建的网站如何实现调用其他网站文章的功能呢?今天给大家分享一下RSS订阅功能。我们可以通过RSS功能调用其他网站的文章自动显示在我们自己的网站上,并且可以自动更新。
" />
那怎么办呢?方法很简单,只需要将下面的代码放在你的网站显示文章列表的位置即可。
</a>
" />
注意:代码中的网站需要修改为调用网站的URL。如果你的网站想放更多本站的文章,只需要复制这段代码,修改里面的网址即可。
注:本文为星速云原创版权,禁止转载。一经发现,追究版权责任!