zoukankan      html  css  js  c++  java
  • 使用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

  • 相关阅读:
    项目是使用 Microsoft.AspNetCore.App 版本 2.1.20 还原的, 但使用当前设置, 将改用版本
    vs发布排除 文件
    win10部分低功耗蓝牙找不到
    解决JLINK_v8灯不亮 jtag 提示无法识别USB设备
    C# BYTE[] 与16进制字符串互相转换
    【转】阿里云证书资源包申请免费SSL流程(图文教程) 【免费证书申请将切换到证书资源包下】
    使用ApkTool以及dex2jar对apk进行反编译-更新异常以及解决方案
    使用ApkTool
    安装纯净版xp,,优盘装系统提示INF file txtsetup.sif的解决方法
    应急灾害管理相关英文关键词梳理--仅作为笔记
  • 原文地址:https://www.cnblogs.com/hejunlin/p/12615657.html
Copyright © 2011-2022 走看看