解决方案: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。如果你的网站想放更多本站的文章,只需要复制这段代码,修改里面的网址即可。

  注:本文为星速云原创版权,禁止转载。一经发现,追究版权责任!

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线