抱歉,你查看的文章不存在

使用MediaCodec和RTMP做直播推流

目前开源的项目或市面上的Android直播客户端主要是用ffmpeg来实现推流的。本文将介绍使用Android原生的视频编码类MediaCodec实现直播推流。

数据流及大致原理

这里所说的直播,就是将你的客户端产生的视频数据,实时发送到服务器上。服务器上的数据再实时地发送到播放客户端上。

以视频数据为例:

  • 获取Camera画面
  • 首先是摄像头拍摄得到原始画面数据,这里原始画面数据的格式我们不用管,因为我们使用的是MediaCodec,所以我们会使用
camera.setPreviewTexture(surfaceTexture)
  • 来利用Camera获取到的画面。

此处的原理可忽略,大致说明的话,就是Camera会把获得的画面保存为OpenGL的一个纹理,我们使用这个纹理就能使用Camera的画面。

绘制画面

在获得画面之后,我们要把这个画面(纹理)“画”到MediaCodec上。

如何画? MediaCodec提供一张’白纸’,也就是一个Surface,供我们把纹理画到上面。此处的API是 MediaCodec.createInputSurface()

怎么画?用Canvas画。当然不是一般的Canvas,我用了这个开源项目android-openGL-canvas。

H264数据

画上去后,MediaCodec就会帮我们把原始画面数据,压缩成相应的视频数据,目前我这里是压缩成H264数据。 所谓的H264数据,其实只是一堆堆的byte[]数组。在项目的例子,我把H264数据写成了文件,可以用某些播放器播放(例如PotPlayer)。

RTMP

我使用了一个开源项目,可以将视频数据封成RTMP包,发送到服务器上。 LibRtmp-Client-for-Android

总结

数据流可以这样看 Camera -> SurfaceTexture -> Surface -> MediaCodec -> encode data(byte[]) -> RTMPMuxer -> Server

音频数据:

相对简单一些,就是从AudioRecord里获取原始音频数据(byte[]),编码成AAC数据(也是byte[]),然后给RTMPMuxer,封装成RTMP包,发到服务器

麦克风MIC -> AudioRecord -> voice data(就是byte[]) -> MediaCodec -> encode data(就是byte[]) -> RTMPMuxer -> Server

Muxer 前面有提到有视频的RTMP包和音频的RTMP包,分别是将单元H264和单元AAC封装成RTMP包,发到服务器。这些包之间有什么规律? 这些包之间是按时间顺序排列的,MediaCodec返回编码数据时,会返回编码数据的时间戳。但注意编码成RTMP包时,取的是相对时间戳,也就是说取到时间戳时,需要计算与上一个包的时间戳的差值,写到RTMP包里。

另外RTMP流本质上是FLV格式的音视频,这里也提供了写成FLV文件的功能。

效果图

Android推流端

视频帧图像处理 前面提到视频帧的图像处理,实际上也是利用了android-openGL-canvas。

关键代码如下:

    ...
    streamPublisher.prepareEncoder(streamPublisherParam, new H264Encoder.OnDrawListener() {
        @Override
        public void onGLDraw(ICanvasGL canvasGL, SurfaceTexture surfaceTexture, RawTexture rawTexture, @Nullable SurfaceTexture outsideSurfaceTexture, @Nullable BasicTexture outsideTexture) {
            drawVideoFrame(canvasGL, outsideSurfaceTexture, outsideTexture);

            Loggers.i("DEBUG", "gl draw");
        }
    });
    ...

    private void drawVideoFrame(ICanvasGL canvasGL, @Nullable SurfaceTexture outsideSurfaceTexture, @Nullable BasicTexture outsideTexture) {
        // Here you can do video process
        // 此处可以视频处理,例如加水印等等
        TextureFilter textureFilterLT = new BasicTextureFilter();
        TextureFilter textureFilterRT = new HueFilter(180);
        int width = outsideTexture.getWidth();
        int height = outsideTexture.getHeight();
        canvasGL.drawSurfaceTexture(outsideTexture, outsideSurfaceTexture, 0, 0, width /2, height /2, textureFilterLT);
        canvasGL.drawSurfaceTexture(outsideTexture, outsideSurfaceTexture, 0, height/2, width/2, height, textureFilterRT);

    }
    ...

如上所示,可以使用各种Filter实现对视频帧图像的处理。总而言之,可以像Canvas那样在视频帧上绘制各种东西。当然要在图上画文字就只能用bitmap代替了。

码率bit/s 在使用MediaCodec时,需要设置码率。这个码率是根据视频分辨率,色彩格式算出来的。

    public H264Encoder(int width, int height, int bitRate, int frameRate, int iframeInterval, final EglContextWrapper eglCtx) throws IOException

其中bitRate就是码率,单位bit/s

一些计算方法可以参考此文: What bitrate should I use when encoding my video? Output size Bitrate Filesize 320x240 pixels 400 kbps 3MB / minute 480x270 pixels 700 kbps 5MB / minute 1024 x 576 pixels 1500 kbps 11MB / minute 1280x720 pixels 2500 kbps 19MB / minute 1920x1080 pixels 4000 kbps 30MB / minute

此方法大部分情况下够用,但是对于复杂视频处理还欠缺。

例如 对比下图的无处理效果(一张纹理)

对于下图这样处理效果(2个画面用的是与上图同样大小的纹理,虽然我设置显示的尺寸不一样),码率是上图的2倍左右。

测试服务器

需要测试的话,请自行搭建RTMP服务器。我用的是自己搭建的Nginx服务器,用的Module是nginx-rtmp-module。搭建服务器不需要写代码,根据教程敲几行命令就行。可以用开源直播软件OBS对比播放效果。 播放器用各种都行,VLC,PotPlayer,ffplay都可以。我用的是ffplay,注意,因为只是简单的服务器,所以要先开播放器连接后再开始启动推流。我使用的命令是 .\ffplay.exe "rtmp://localhost:19305/live/room live=1"

作者:chillingvan 链接:https://www.jianshu.com/p/3c479c0f4876

原文发布于微信公众号 - 何俊林(DriodDeveloper)

原文发表时间:2018-08-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

码农突围

0 篇文章93 人订阅

相关文章

来自专栏大数据挖掘DT机器学习

R语言爬虫初尝试-基于RVEST包学习

在学完coursera的getting and Cleaning data后,继续学习用R弄爬虫网络爬虫。主要用的还是Hadley Wickham开发的rves...

41830
来自专栏嵌入式程序猿

怎么样用万用表检测IGBT

IGBT是变频器中非常重要的功率器件,这段时间给大家推送了多篇变频器拆解,设计的文章,而功率器件的好坏是检测中非常重要的一环,那么如何用万用表检测呢,我们以英飞...

60830
来自专栏睿哥杂货铺

我的写作工具链

1)源文件持续修改中出现的回归编辑工作,互相引用的链接较多

29390
来自专栏Data Analysis & Viz

手把手教你完成一个数据科学小项目(3):数据异常与清洗

本系列将全面涉及本项目从爬虫、数据提取与准备、数据异常发现与清洗、分析与可视化等细节,并将代码统一开源在GitHub:DesertsX/gulius-proje...

14630
来自专栏SDNLAB

对OpenFlow下的一种网络安全应用模型(OFX)的思考

最近有时间学习了一篇发表在NSDI’16的叫做 ”Enabling Practical Software-defined Networking Security...

35770
来自专栏AI启蒙研究院

75亿美元之后,再也不能自由的同性交友了

12420
来自专栏阮一峰的网络日志

学习Javascript的书籍

昨天,ppip同学留言: 你的js主要是用什么材料学的?推荐用哪本教程呢? 我想了一下,发现自己还真的读过不少书。我在这里做一个总结,希望对想学习Javasc...

31950
来自专栏吉浦迅科技

AMD 发布APP SDK 3.0 Beta

AMD刚刚发布的驱动程序支持最新的Khronos OpenCL™2.0标准驱动。这被看作提高异构计算加速路径的巨大里程碑,OpenCL2.0实现了许...

32460
来自专栏游戏开发那些事

【python游戏编程之旅】第五篇---嗷大喵爱吃鱼小游戏开发实例

本系列博客介绍以python+pygame库进行小游戏的开发。有写的不对之处还望各位海涵。

15220
来自专栏ionic3+

hybird,关于地理定位

原生定位使用GPS/北斗定位,网页定位仅支持基站定位和WIFI定位两种方式,自然优先选型原生定位,但是,为了在一些影响GPS信号接收的地方提高精度,现在的第三方...

25530

扫码关注云+社区

领取腾讯云代金券