精华内容
下载资源
问答
  • EasyRTMP EasyRTMP是什么? EasyRTMP是一个EasyDarwin配套使用,也可以单独使用的RTMP推送库,通过EasyRTMP我们就可以避免接触到稍显复杂的RTMP推送流程,只需要调用EasyRTMP的几个API接口,就能轻松、稳定地把流...
  • # EasyRTMP_RTSP 基于Android手机、设备拉取远程RTSP流到本地,再转成RTMP,推送到标准的RTMP服务器(例如EasyDSS、nginx-rtmp、crtmpserver等等)。其他平台RTSP转RTMP参考https://github.com/EasyDarwin/EasyRTMP
  • EasyRTMP 最简单的流媒体平台框架 帮助流媒体开发者和创业型企业快速构建流媒体服务平台,更快、更简单地实现最新的移动互联网流媒体直播与点播.
  • EasyRTMP是由青犀开放平台开发的一套RTMP直播推送功能组件,内部集成了包括:基本RTMP协议、断线重连、异步推送、环形缓冲区、推送网络拥塞自动丢帧、缓冲区关键帧检索、事件回调(断线、音视频数据回调),通过...

    EasyRTMP是什么?

    EasyRTMP是由青犀开放平台开发的一套RTMP直播推送功能组件,内部集成了包括:基本RTMP协议、断线重连、异步推送、环形缓冲区、推送网络拥塞自动丢帧、缓冲区关键帧检索、事件回调(断线、音视频数据回调),通过EasyRTMP我们就可以避免接触到稍显复杂的RTMP推送或者客户端流程,只需要调用EasyRTMP的几个API接口,就能轻松、稳定地进行流媒体音视频数据的推送,支持市面上绝大部分的RTMP流媒体服务器,包括Wowza、Red5、nginx-rtmp、crtmpserver等主流RTMP服务器,全平台支持:Windows、Linux、ARM(各种交叉编译工具链)、Android、iOS。

    我们有专门的使用EasyRTMP library 实现的安卓版本的EasyRTMP,可直接将手机摄像头或者桌面以RTMP协议推送到相关服务器,简单易用、易于集成。EasyRTMP的APP界面如图所示:
    Android EasyRTMP运行界面
    可以看到,在首页有如下的功能元素:

    • 切换分辨率
    • 录像
    • 切换摄像头
    • 帧率码率显示
    • 开启、关闭摄像头推送
    • 开启、关闭屏幕推送
    • 设置

    在此逐一介绍下:

    切换分辨率

    切换分辨率,即切换摄像头的预览分辨率。
    首先,通过如下方式获取到摄像头支持的分辨率:

      ```
      List<Camera.Size> supportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
      ```
    

    通过如下方式设置预览分辨率:

      ```
      Camera.Parameters parameters = mCamera.getParameters();
      parameters.setPreviewSize(width, height);
      ...
      mCamera.setParameters(parameters);
      ```
    

    切换分辨率时,先关闭预览重新设置分辨率,再开启预览即可:

      ```
      mCamera.stopPreview();
      // 设置新分辨率
      ...
      mCamera.startPreview();
      ```
    

    录像

    录像是通过EasyMuxer来进行录像的,EasyMuxer是对MediaMuxer进行的一个封装,通过简单的一些接口即可进行便捷录像、更换文件、关闭录像等功能。同时,EasyMuxer可对PCM音频格式进行录像,这样任何类型的音频编码数据都可以在先解码后,再编成AAC来录像成MP4格式。EasyMuxer的介绍见博客文章:Android MediaMuxer录像(支持G711音频)

    切换摄像头

    切换摄像头跟切换分辨率类似,都是先把当前预览关闭再重新打开。不同的是,切换摄像头时,需要将摄像头release再重新open,而切换分辨率只用stopPreview再startPreview即可。

      ```
      stopPreview();
      destroyCamera();
      if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
          //现在是后置,变更为前置
          if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK后置
        mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
        createCamera();
        startPreview();
        break;
          }
      } else {
          //现在是前置, 变更为后置
          if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK后置
        mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
        createCamera();
        startPreview();
        break;
          }
      }
      ```
    

    显示帧率码率

    为了方便处理,帧率码率的统计放在了推送端,即统计某一时间段内发送的视频帧数和字节数,然后除以时间段得出结果。这里使用EventBus POST出去即可,上层捕获到该Event后进行展示。

      ```
      mTotal += length;
      if (type == 1){
          mTotalFrms++;
      }
      long interval = System.currentTimeMillis() - pPreviewTS;
      if (interval >= 3000){
          long bps = mTotal * 1000 / (interval);
          long fps = mTotalFrms * 1000 / (interval);
          Log.i(TAG, String.format("bps:%d, fps:%d", fps, bps));
          pPreviewTS = System.currentTimeMillis();
          mTotal = 0;
          mTotalFrms = 0;
    
          BUS.post(new StreamStat((int)fps, (int)bps));
      }
      ```
    

    上层捕获并显示:

      ```
      @Subscribe
      public void onStreamStat(final StreamStat stat) {
          streamStat.post(new Runnable() {
              @Override
              public void run() {
                  streamStat.setText(getString(R.string.stream_stat, stat.fps, stat.bps / 1024));
              }
          });
      }
      ```
    

    开启、关闭摄像头推送

    当未推送的时候,点击按钮即可发起推送,这时候初始化Pusher库,初始化成功后,得到Pusher句柄,后续的Push接口里会根据该值来判断是否进行真正的推送动作。
    当推送开始时,点击按钮可关闭推送,相应地进行反初始化,这样在Push接口就不会进行推送了。

    开启、关闭屏幕推送

    推送屏幕主要在RecordService服务里进行的,点击推送按钮时先开启服务:

      ```
      Intent intent = new Intent(getApplicationContext(), RecordService.class);
      startService(intent);
      ```
    

    具体来说,MediaProjection 类可以将当前屏幕画面采集到一个surface里面,而MediaCodec可以从一个surface里面获取视频数据源。我们让MediaProjection投射到MediaCodec创建的Surface,MediaCodec就可以获取到MediaProjection投射的视频了。

    关闭屏幕推送时停止服务器,在服务内部 onDestory时反初始化相关资源。
    有关屏幕推送的描述详见:
    Android MediaMuxer录像(支持G711音频)

    设置

    设置里面的功能如下图所示:

    tsingsee

    • URL可以设置要推送的流地址,客户端亦可用改地址进行播放;
    • 是否能使用摄像头后台采集?如果能用,推送将在APP转到后台时继续;
    • 是否使用软编码?如果能用,那么app会使用x264软编码推送;
    • 叠加水印如果能用, 那么视频上面会叠加一些水印信息(目前可现实文字水印,文字内容在APP内可以设置);
    • 仅推送音频如果能用,那则不会推送视频,仅推送声音;
    • 打开录像文件夹点击后会打开在APP内产生的录像片段,可以进行便捷查看;
    • 版本号显示;
    • 保存设置并推出设置界面。

    使用EasyRTMP推流组件完整方案

    EasyDSS商用流媒体服务器(下载地址)是EasyDarwin流媒体团队开发的一款支持视频点播、转码、RTMP推流直播、RTMP/HLS直播分发、服务端录像、录像检索、录像下载、时移回放的商用流媒体服务器,采用业界优秀的流媒体框架模式设计,服务运行高效、稳定、可靠、易维护,支持RTMP直播、RTMP推送、HTTP点播、HLS直播,并支持关键帧缓冲,画面秒开等多种特性,能够接入WEB、Android、iOS、微信等全平台客户端,是移动互联网时代贴近企业点播/直播需求的一款接地气的流媒体服务器,配套OBS、EasyRTMP等直播推流工具以及EasyPlayer等网络播放器,可以形成一套完整的视频直播、录播解决方案,满足用户在各种行业场景的流媒体业务需求。

    tsingsee
    tsingsee

    展开全文
  • EasyRTMP介绍EasyRTMP是结合了多种音视频缓存及网络技术的一个rtmp直播推流端,包括:圆形缓冲区(circular buffer)、智能丢帧、自动重连、rtmp协议等等多种技术,能够非常有效地适应各种平台(Windows、Linux、ARM、...

    EasyRTMP介绍

    EasyRTMP是结合了多种音视频缓存及网络技术的一个rtmp直播推流端,包括:圆形缓冲区(circular buffer)、智能丢帧、自动重连、rtmp协议等等多种技术,能够非常有效地适应各种平台(Windows、Linux、ARM、Android、iOS),各种网络环境(有线、wifi、4G),以及各种情况下的直播恢复(服务器重启、网络重启、硬件设备重启),今天我们讲解的是EasyRTMP中rtmp推送连接的建立、推送H264+AAC音视频、以及rtmp推送重连过程的详细讲解;

    librtmp实现的RTMP基本协议

    EasyRTMP中RTMP推送流程依然采用的是业界良心的librtmp,其稳定性和可靠性毋庸置疑,已经得到了广大的开发者的验证,所以EasyRTMP也直接采用了librtmp,librtmp的下载可以到:http://rtmpdump.mplayerhq.hu/ 下载版本,直入主题,我们对librtmp的代码进行分析,librtmp的推送流程主要包括:握手、分块、Connect、Metadata、SendStream(Video&Audio)、Close等等流程,我们引用了博客http://blog.csdn.net/leixiaohua1020/article/details/42105049中对RTMP推送流程的解析:

    EasyRTMP

    整个程序包含3个接口函数:
    RTMP264_Connect():建立RTMP连接。
    RTMP264_Send():发送数据。
    RTMP264_Close():关闭RTMP连接。
    按照顺序调用上述3个接口函数就可以完成H.264码流的发送。

    结构图中关键函数的作用如下所列。

    RTMP264_Connect()中包含以下函数:
    InitSockets():初始化Socket
    RTMP_Alloc():为结构体“RTMP”分配内存。
    RTMP_Init():初始化结构体“RTMP”中的成员变量。
    RTMP_SetupURL():设置输入的RTMP连接的URL。
    RTMP_EnableWrite():发布流的时候必须要使用。如果不使用则代表接收流。
    RTMP_Connect():建立RTMP连接,创建一个RTMP协议规范中的NetConnection。
    RTMP_ConnectStream():创建一个RTMP协议规范中的NetStream。

    RTMP264_Send()中包含以下函数:
    ReadFirstNaluFromBuf():从内存中读取出第一个NAL单元
    ReadOneNaluFromBuf():从内存中读取出一个NAL单元
    h264_decode_sps():解码SPS,获取视频的宽,高,帧率信息
    SendH264Packet():发送一个NAL单元

    SendH264Packet()中包含以下函数:
    SendVideoSpsPps():如果是关键帧,则在发送该帧之前先发送SPS和PPS
    SendPacket():组装一个RTMPPacket,调用RTMP_SendPacket()发送出去
    RTMP_SendPacket():发送一个RTMP数据RTMPPacket

    RTMP264_Close()中包含以下函数:
    RTMP_Close():关闭RTMP连接
    RTMP_Free():释放结构体“RTMP”
    CleanupSockets():关闭Socket

    RTMP直播推流中需要注意的点

    这里在简书上有一篇:http://www.jianshu.com/p/00aceabce944 已经说的很到位了,我们直接引用,希望能够给大家带来帮助:

    1. RTMP 握手

    RTMP 握手分为简单握手和复杂握手,现在Adobe公司使用RTMP协议的产品应该用的都是复杂握手,这里不介绍,只说简单握手。 按照网上的说法RTMP握手的过程如下

    1. 握手开始于客户端发送C0、C1块。服务器收到C0或C1后发送S0和S1。
    2. 当客户端收齐S0和S1后,开始发送C2。当服务器收齐C0和C1后,开始发送S2。
    3. 当客户端和服务器分别收到S2和C2后,握手完成。

    在实际工程应用中,一般是客户端先将C0, C1块同时发出,服务器在收到C1 之后同时将S0, S1, S2发给客户端。S2的内容就是收到的C1块的内容。之后客户端收到S1块,并原样返回给服务器,简单握手完成。按照RTMP协议个要求,客户端需要校验C1块的内容和S2块的内容是否相同,相同的话才彻底完成握手过程,实际编写程序用一般都不去做校验。

    RTMP握手的这个过程就是完成了两件事:1. 校验客户端和服务器端RTMP协议版本号,2. 是发了一堆数据,猜想应该是测试一下网络状况,看看有没有传错或者不能传的情况。RTMP握手是整个RTMP协议中最容易实现的一步,接下来才是大头。

    2. RTMP 分块

    创建RTMP连接算是比较难的地方,开始涉及消息分块(chunking)和 AFM(也是Adobe家的东西)格式数据的一些东西,在上面提到的文章中也有介绍为什要进行RTMP分块。

    Chunk Size
    RTMP是按照chunk size进行分块,chunk size指的是 chunk的payload部分的大小,不包括chunk basic header 和 chunk message header,即chunk的body的大小。客户端和服务器端各自维护了两个chunk size, 分别是自身分块的chunk size 和 对端 的chunk size, 默认的这两个chunk size都是128字节。通过向对端发送set chunk size 消息告知对方更改了 chunk size的大小,即告诉对端:我接下来要以xxx个字节拆分RTMP消息,你在接收到消息的时候就按照新的chunk size 来组包。
    在实际写代码的时候一般会把chunk size设置的很大,有的会设置为4096,FFMPEG推流的时候设置的是 60*1000,这样设置的好处是避免了频繁的拆包组包,占用过多的CPU。设置太大的话也不好,一个很大的包如果发错了,或者丢失了,播放端就会出现长时间的花屏或者黑屏等现象。
    
    Chunk Type
    RTMP 分成的Chunk有4中类型,可以通过 chunk basic header的 高两位指定,一般在拆包的时候会把一个RTMP消息拆成以 Type_0 类型开始的chunk,之后的包拆成 Type_3 类型的chunk,我查看了有不少代码也是这样实现的,这样也是最简单的实现。
    RTMP 中关于Message 分chunk只举了两个例子,这两个例子不是很具有代表性。假如第二个message和第一个message的message stream ID 相同,并且第二个message的长度也大于了chunk size,那么该如何拆包?当时查了很多资料,都没有介绍。后来看了一些源码,发现第二个message可以拆成Type_1类型一个chunk, message剩余的部分拆成Type_3类型的chunk。FFMPEG中好像就是这么做的。
    

    3. RTMP 消息

    • Connect消息
      握手之后先发送一个connect 命令消息,命令里面包含什么东西,协议中没有说,真实通信中要指定一些编解码的信息,这些信息是以AMF格式发送的, 下面是用swift 写的connect命令包含的参数信息:

         transactionID += 1 // 0x01
          let command:RTMPCommandMessage = RTMPCommandMessage(commandName: "connect", transactionId: transactionID, messageStreamId: 0x00)
          let objects:Amf0Object = Amf0Object()
          objects.setProperties("app", value: rtmpSocket.appName)
          objects.setProperties("flashVer",value: "FMLE/3.0 (compatible; FMSc/1.0)")
          objects.setProperties("swfUrl", value:"")
          objects.setProperties("tcUrl", value: "rtmp://" + rtmpSocket.hostname + "/" + rtmpSocket.appName)
          objects.setProperties("fpad", value: false)
          objects.setProperties("capabilities", value:239)
          objects.setProperties("audioCodecs", value:3575)
          objects.setProperties("videoCodecs", value:252)
          objects.setProperties("videoFunction",value: 1)
          objects.setProperties("pageUrl",value: "")
          objects.setProperties("objectEncoding",value: 0)
      

    服务器返回的是一个_result命令类型消息,这个消息的payload length一般不会大于128字节,但是在最新的nginx-rtmp中返回的消息长度会大于128字节,所以一定要做好收包,组包的工作。
    关于消息的transactionID是用来标识command类型的消息的,服务器返回的_result消息可以通过 transactionID来区分是对哪个命令的回应,connect 命令发完之后还要发送其他命令消息,要保证他们的transactionID不相同。
    发送完connect命令之后一般会发一个 set chunk size消息来设置chunk size 的大小,也可以不发。
    Window Acknowledgement Size 是设置接收端消息窗口大小,一般是2500000字节,即告诉客户端你在收到我设置的窗口大小的这么多数据之后给我返回一个ACK消息,告诉我你收到了这么多消息。在实际做推流的时候推流端要接收很少的服务器数据,远远到达不了窗口大小,所以基本不用考虑这点。而对于服务器返回的ACK消息一般也不做处理,我们默认服务器都已经收到了这么多消息。
    之后要等待服务器对于connect的回应的,一般是把服务器返回的chunk都读完组成完整的RTMP消息,没有错误就可以进行下一步了。

    • Create Stream 消息

    创建完RTMP连接之后就可以创建RTMP流,客户端要想服务器发送一个releaseStream命令消息,之后是FCPublish命令消息,在之后是createStream命令消息。当发送完createStream消息之后,解析服务器返回的消息会得到一个stream ID, 这个ID也就是以后和服务器通信的 message stream ID, 一般返回的是1,不固定。

    • Publish Stream

    推流准备工作的最后一步是 Publish Stream,即向服务器发一个publish命令,这个命令的message stream ID 就是上面 create stream 之后服务器返回的stream ID,发完这个命令一般不用等待服务器返回的回应,直接下一步发送音视频数据。有些rtmp库 还会发setMetaData消息,这个消息可以发也可以不发,里面包含了一些音视频编码的信息。

    4. 发布音视频

    当以上工作都完成的时候,就可以发送音视频了。音视频RTMP消息的Payload中都放的是按照FLV-TAG格式封的音视频包,具体可以参照FLV协议文档。

    5. 关于RTMP的时间戳

    RTMP的时间戳在发送音视频之前都为零,开始发送音视频消息的时候只要保证时间戳是单增的基本就可以正常播放音视频。我读Srs-librtmp的源码,发现他们是用h264的dts作为时间戳的。我在用java写的时候是先获取了下当前系统时间,然后每次发送消息的时候都与这个起始时间相减,得到时间戳。

    6. 关于Chunk Stream ID

    RTMP 的Chunk Steam ID是用来区分某一个chunk是属于哪一个message的 ,0和1是保留的。每次在发送一个不同类型的RTMP消息时都要有不用的chunk stream ID, 如上一个Message 是command类型的,之后要发送视频类型的消息,视频消息的chunk stream ID 要保证和上面 command类型的消息不同。每一种消息类型的起始chunk 的类型必须是 Type_0 类型的,表明我是一个新的消息的起始。

    EasyRTMP实现的重连过程

    EasyRTMP对librtmp实现了一层封装,不断会检测librtmp直播推送当前的RTMP连接状态,当检测到librtmp连接与RTMP流媒体服务器断开的时候,我们会重新进行整个RTMP的握手、Connect、metadata的流程,这样就能够保证在没有人为干预的情况下,EasyRTMP对外能提供稳定的推送服务,外部只需要考虑将生产者生产的音视频数据源源不断地往EasyRTMP接口进行推送就可以了,而且EasyRTMP的音视频推送接口也是采用的异步模式,内部线程进行发送处理,这样就保证了外围调用者的工作效率,不会像直接调用librtmp那样,在网络较差的情况下,会出现发送阻塞等问题!

    Github

    https://github.com/EasyDarwin/EasyRTMP

    获取更多信息

    邮件:support@easydarwin.org

    WEB:www.EasyDarwin.org

    Copyright © EasyDarwin.org 2012-2016

    EasyDarwin

    展开全文
  • Android EasyRTMP 介绍

    千次阅读 2017-10-07 11:54:30
    EasyRTMP是一个EasyDarwin配套使用,也可以单独使用的RTMP推送库,通过EasyRTMP我们就可以避免接触到稍显复杂的RTMP推送流程,只需要调用EasyRTMP的几个API接口,就能轻松、稳定地把流媒体音视频数据推送给Red5、...

    EasyRTMP是什么?

    EasyRTMP是一个EasyDarwin配套使用,也可以单独使用的RTMP推送库,通过EasyRTMP我们就可以避免接触到稍显复杂的RTMP推送流程,只需要调用EasyRTMP的几个API接口,就能轻松、稳定地把流媒体音视频数据推送给Red5、Ngnix、crtmpserver等RTMP服务器.

    我们有专门的使用EasyRTMP library 实现的安卓版本的EasyRTMP,可直接将手机摄像头或者桌面以RTMP协议推送到相关服务器,简单易用,易于集成.EasyRTMP的APP界面如图所示:

    Android EasyRTMP运行界面

    可以看到,在首页有如下的功能元素
    - 切换分辨率
    - 录像
    - 切换摄像头
    - 帧率码率显示
    - 开启,关闭摄像头推送
    - 开启,关闭屏幕推送
    - 设置

    在此逐一介绍下:

    切换分辨率

    切换分辨率,即切换摄像头的预览分辨率.首先需要获取到摄像头支持的分辨率.通过如下方式获取到摄像头支持的分辨率:

    List<Camera.Size> supportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();

    通过如下方式设置预览分辨率:

    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(width, height);
    ...
    mCamera.setParameters(parameters);

    切换分辨率时,先关闭预览,重新设置分辨率,再开启预览即可:

    mCamera.stopPreview();
    // 设置新分辨率
    ...
    mCamera.startPreview();

    录像

    录像是通过EasyMuxer来进行录像的.EasyMuxer是对MediaMuxer进行的一个封装,通过简单的一些接口即可进行便捷录像\更换文件\关闭录像等功能.同时,EasyMuxer可对PCM音频格式进行录像,这样任何类型的音频编码数据都可以在先解码后,再编成AAC来录像成.MP4格式.EasyMuxer的介绍见博客文章:http://blog.csdn.net/jyt0551/article/details/72787095

    切换摄像头

    切换摄像头跟切换分辨率类似,都是先要把当前预览关闭,再重新打开.不同的是,切换摄像头时,需要将摄像头release再重新open,而切换分辨率只用stopPreview再startPreview即可.

    stopPreview();
    destroyCamera();
    if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        //现在是后置,变更为前置
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK后置
            mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
            createCamera();
            startPreview();
            break;
        }
    } else {
        //现在是前置, 变更为后置
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK后置
            mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
            createCamera();
            startPreview();
            break;
        }
    }

    显示帧率码率

    为了方便处理,帧率码率的统计放在了推送端.即统计某一时间段内发送的视频帧数和字节数,然后除以时间段得出结果.这里使用EventBus POST出去即可.上层捕获到该Event后,进行展示.

    mTotal += length;
    if (type == 1){
        mTotalFrms++;
    }
    long interval = System.currentTimeMillis() - pPreviewTS;
    if (interval >= 3000){
        long bps = mTotal * 1000 / (interval);
        long fps = mTotalFrms * 1000 / (interval);
        Log.i(TAG, String.format("bps:%d, fps:%d", fps, bps));
        pPreviewTS = System.currentTimeMillis();
        mTotal = 0;
        mTotalFrms = 0;
    
        BUS.post(new StreamStat((int)fps, (int)bps));
    }

    上层捕获并显示:

    @Subscribe
    public void onStreamStat(final StreamStat stat) {
        streamStat.post(new Runnable() {
            @Override
            public void run() {
                streamStat.setText(getString(R.string.stream_stat, stat.fps, stat.bps / 1024));
            }
        });
    }

    开启,关闭摄像头推送

    当未推送的时候,点击按钮即可发起推送.这时候,初始化Pusher库,初始化成功后,得到Pusher句柄,后续的Push接口里会根据该值来判断是否进行真正的推送动作.
    当推送开始时,点击按钮可关闭推送.相应地进行反初始化,这样在Push接口就不会进行推送了.

    开启,关闭屏幕推送

    推送屏幕主要在RecordService服务里进行的.点击推送按钮时,先开启服务:

    Intent intent = new Intent(getApplicationContext(), RecordService.class);
    startService(intent);

    具体来说,MediaProjection 类可以将当前屏幕画面采集到一个surface里面,而MediaCodec可以从一个surface里面获取视频数据源。我们让MediaProjection投射到MediaCodec创建的Surface,MediaCodec就可以获取到MediaProjection投射的视频了.

    关闭屏幕推送时,停止服务器,在服务内部 onDestory时,反初始化相关资源.
    有关屏幕推送更详细的描述,见:
    http://blog.csdn.net/jyt0551/article/details/72787095

    设置

    设置里面的功能如下图所示:

    这里写图片描述

    • URL可以设置要推送的流地址,客户端亦可用改地址进行播放;
    • 是否使能摄像头后台采集.如果使能的话,推送将在APP转到后台时继续.
    • 是否使用软编码?如果使能,那么app会使用x264软编码推送
    • 叠加水印 如果使能, 那么视频上面会叠加一些水印信息.目前可现实文字水印,文字内容在APP内可以设置
    • 仅推送音频 如果使能,那则不会推送视频,仅推送声音.
    • 打开录像文件夹 点击后会打开在APP内产生的录像片段,可以进行便捷查看
    • 版本号显示
    • 保存设置并推出设置界面

    EasyRTMP项目介绍

    EasyRTMP是EasyDarwin团队开发的一套RTMP直播推送功能组件,内部集成了包括:基本RTMP协议、断线重连、异步推送、环形缓冲区、推送网络拥塞自动丢帧、缓冲区关键帧检索、事件回调(断线、音视频数据回调),通过EasyRTMP我们就可以避免接触到稍显复杂的RTMP推送或者客户端流程,只需要调用EasyRTMP的几个API接口,就能轻松、稳定地进行流媒体音视频数据的推送,支持市面上绝大部分的RTMP流媒体服务器,包括Red5、Ngnix_rtmp、crtmpserver等主流RTMP服务器,全平台支持:Windows、Linux、ARM(各种交叉编译工具链)、Android、iOS;

    EasyRTMP项目地址:https://github.com/EasyDarwin/EasyRTMP

    获取更多信息

    邮件:support@easydarwin.org

    WEB:www.EasyDarwin.org

    Copyright © EasyDarwin.org 2012-2017

    EasyDarwin

    展开全文
  • EasyRTMP 1.3.17.1014版本更新这个版本主要更新了如下内容: 关于界面调整.由于业务需求,app的关于界面需要同时起到我们产品宣传的作用,因此我们决定将关于界面内容更完善些,于是乎就改成如下这样子: 这个界面使用了...

    EasyRTMP 1.3.17.1014版本更新

    这个版本主要更新了如下内容:

    • 关于界面调整.

      由于业务需求,app的关于界面需要同时起到我们产品宣传的作用,因此我们决定将关于界面内容更完善些,于是乎就改成如下这样子:
      关于界面

      这个界面使用了ConstrantLayout进行布局.其代码如下:

      <?xml version="1.0" encoding="utf-8"?>
      
      <layout xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <android.support.constraint.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
      
      
                <TextView
                    android:text="EasyRTMP是一套调用简单、功能完善、运行高效稳定的RTMP功能组件,经过多年实战和线上运行打造,支持RTMP推送断线重连、环形缓冲、智能丢帧、网络事件回调,支持Windows、Linux、arm(hisiv100/hisiv200/hisiv300/hisiv400/etc..)、Android、iOS平台,支持市面上绝大部分的RTMP流媒体服务器,能够完美应用于各种行业的直播需求,手机直播、桌面直播、摄像机直播、课堂直播等等方面!\n配套方案推荐:"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:id="@+id/desc"
                    android:layout_marginTop="16dp"
                    app:layout_constraintTop_toBottomOf="@+id/toolbar"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toRightOf="parent"
                    android:layout_marginStart="16dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginEnd="16dp"
                    android:layout_marginRight="16dp" />
      
                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="16dp"
                    android:layout_marginTop="8dp"
                    android:id="@+id/rtmp_title"
                    android:layout_marginRight="16dp"
                    android:text="-EasyRTMP推流组件:"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/desc" />
      
                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="156dp"
                    android:layout_height="156dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_marginTop="8dp"
                    android:src="@drawable/android_rtmp"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toLeftOf="@+id/guideline"
                    app:layout_constraintTop_toBottomOf="@+id/rtmp_title" />
      
                <TextView
                    android:layout_width="wrap_content"
                    android:text="Android"
                    app:layout_constraintTop_toBottomOf="@+id/imageView"
                    android:layout_marginStart="16dp"
                    app:layout_constraintLeft_toLeftOf="parent"
                    android:layout_marginLeft="16dp"
                    android:layout_marginEnd="16dp"
                    android:id="@+id/android_rtmp_title"
                    android:layout_marginTop="8dp"
                    app:layout_constraintRight_toLeftOf="@+id/guideline"
                    android:layout_height="wrap_content" />
      
                <android.support.constraint.Guideline
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/guideline"
                    app:layout_constraintGuide_percent="0.5"
                    android:orientation="vertical" />
      
      
                <ImageView
                    android:id="@+id/imageView2"
                    android:layout_width="156dp"
                    android:layout_height="156dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_marginTop="8dp"
                    android:src="@drawable/ios_rtmp"
                    app:layout_constraintLeft_toRightOf="@+id/guideline"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/rtmp_title" />
      
                <TextView
                    android:layout_width="wrap_content"
                    android:text="iOS"
                    app:layout_constraintTop_toBottomOf="@+id/imageView2"
                    android:layout_marginStart="16dp"
                    app:layout_constraintRight_toRightOf="parent"
                    android:layout_marginLeft="16dp"
                    android:layout_marginEnd="16dp"
                    android:layout_marginTop="8dp"
                    app:layout_constraintLeft_toRightOf="@+id/guideline"
                    android:layout_height="wrap_content" />
      
                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="16dp"
                    android:layout_marginTop="8dp"
                    android:id="@+id/server_title"
                    android:layout_marginRight="16dp"
                    android:text="-EasyDSS RTMP流媒体服务器:\nhttp://www.easydss.com"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/android_rtmp_title" />
                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="16dp"
                    android:layout_marginTop="8dp"
                    android:id="@+id/player_title"
                    android:layout_marginRight="16dp"
                    android:text="-EasyPlayerPro全功能播放器:\nhttps://github.com/EasyDSS/EasyPlayerPro"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/server_title" />
      
      
                <ImageView
                    android:id="@+id/imageView_player_pro_android"
                    android:layout_width="156dp"
                    android:layout_height="156dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_marginTop="8dp"
                    android:src="@drawable/android_player_pro"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toLeftOf="@+id/guideline"
                    app:layout_constraintTop_toBottomOf="@+id/player_title" />
      
                <TextView
                    android:layout_width="wrap_content"
                    android:text="Android"
                    app:layout_constraintTop_toBottomOf="@+id/imageView_player_pro_android"
                    android:layout_marginStart="16dp"
                    app:layout_constraintLeft_toLeftOf="parent"
                    android:layout_marginLeft="16dp"
                    android:layout_marginEnd="16dp"
                    android:id="@+id/android_player_pro_title"
                    android:layout_marginTop="8dp"
                    app:layout_constraintRight_toLeftOf="@+id/guideline"
                    android:layout_height="wrap_content" />
      
      
                <ImageView
                    android:id="@+id/imageView_player_pro_ios"
                    android:layout_width="156dp"
                    android:layout_height="156dp"
                    android:layout_marginLeft="16dp"
                    android:layout_marginRight="16dp"
                    android:layout_marginTop="8dp"
                    android:src="@drawable/ios_player_pro"
                    app:layout_constraintLeft_toRightOf="@+id/guideline"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/player_title" />
      
                <TextView
                    android:layout_width="wrap_content"
                    android:text="iOS"
                    app:layout_constraintTop_toBottomOf="@+id/imageView_player_pro_ios"
                    android:layout_marginStart="16dp"
                    app:layout_constraintRight_toRightOf="parent"
                    android:layout_marginLeft="16dp"
                    android:layout_marginEnd="16dp"
                    android:layout_marginTop="8dp"
                    app:layout_constraintLeft_toRightOf="@+id/guideline"
                    android:layout_height="wrap_content" />
      
                <TextView
                    android:text="Copyright © EasyDarwin.org 2012-2017"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:id="@+id/textView3"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/android_player_pro_title"
                    android:layout_marginBottom="8dp"
                    android:layout_marginStart="16dp"
                    app:layout_constraintLeft_toLeftOf="parent"
                    android:layout_marginLeft="16dp"
                    android:layout_marginEnd="16dp"
                    android:layout_marginTop="16dp"
                    app:layout_constraintRight_toRightOf="parent"
                    android:layout_marginRight="16dp"
                    android:layout_width="0dp" />
      
            </android.support.constraint.ConstraintLayout>
        </ScrollView>
      </layout>
    • 支持x86,mips架构

    • 在6.0及以上系统上,动态获取摄像头以及录音权限.

      安卓6.0以上增加了动态权限的功能.有些权限,比如摄像头录音权限,不仅需要在Manifest里面申请,还需要在APP运行时动态获取.而如果APP的targetVersion值设置在6.0以下,那系统会做兼容,不需要再动态获取即可.这个版本我们适配了动态权限.

      在StreamActivity入口,首先检查是否有了相关权限了?如果没有的话,那就去申请.

      if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
                    ActivityCompat.checkSelfPermission(this, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA_PERMISSION);
                mNeedGrantedPermission = true;
                return;
            } else {
                // resume..
            }

      在发送requestPermissions后,系统会弹出对话框让用户确认,用户确认后会进入onRequestPermissionsResult回调.在回调里面再判断用户是否同意了.

      当用户同意授权后,再启动摄像头进行推送.

    • 在后台上传视频时,需要动态获取”显示在顶层”权限.

      在6.0及以上的系统,对TYPE_SYSTEM_OVERLAY这种格式的VIEW进行了限制,应用程序需要获取“运行在顶层”才可以创建这种view.我们需要创建这种view来进行视频采集.Android提供了方法来判断是否拥有该权限了:

      Settings.canDrawOverlays(this);

      与普通权限获取不一样的是,这种权限需要打开设置界面进行授权,在onActivityResult里面检查授权情况.我们在设置界面,勾选使能后台采集时,向用户申请这种权限.

      相关代码如下:

      if (!Settings.canDrawOverlays(SettingActivity.this)) {
      new AlertDialog.Builder(SettingActivity.this).setTitle("后台上传视频").setMessage("后台上传视频需要APP出现在顶部.是否确定?").setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialogInterface, int i) {
      
          final Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + BuildConfig.APPLICATION_ID));
          startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION);
        }
      }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialogInterface, int i) {
          PreferenceManager.getDefaultSharedPreferences(SettingActivity.this).edit()
            .putBoolean(KEY_ENABLE_BACKGROUND_CAMERA, false).apply();
          buttonView.toggle();
        }
      }).setCancelable(false).show();
      }

      onActivityResult里判断是否获取到了该权限:

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == REQUEST_OVERLAY_PERMISSION) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    boolean canDraw = Settings.canDrawOverlays(this);
                    PreferenceManager.getDefaultSharedPreferences(SettingActivity.this).edit()
                            .putBoolean(KEY_ENABLE_BACKGROUND_CAMERA, canDraw).apply();
                    if (!canDraw){
                        CheckBox backgroundPushing = (CheckBox) findViewById(R.id.enable_background_camera_pushing);
                        backgroundPushing.setChecked(false);
                    }
                }
            }
        }

    关于EasyRTMP推流SDK

    EasyRTMP是一套调用简单、功能完善、运行高效稳定的RTMP功能组件,经过多年实战和线上运行打造,支持RTMP推送断线重连、环形缓冲、智能丢帧、网络事件回调,支持Windows、Linux、arm(hisiv100/hisiv200/hisiv300/hisiv400/etc..)、Android、iOS平台,支持市面上绝大部分的RTMP流媒体服务器,包括Red5、Ngnix_rtmp、crtmpserver等主流RTMP服务器,能够完美应用于各种行业的直播需求,手机直播、桌面直播、摄像机直播、课堂直播等等方面!

    点击链接加入群【EasyRTMP-RTMP直播推送】:587254841

    获取更多信息

    邮件:support@easydarwin.org

    WEB:www.EasyDarwin.org

    Copyright © EasyDarwin.org 2012-2017

    EasyDarwin

    展开全文
  • 虽然网上有很多EasyRTMP实现支持h265推送的资料,但都不尽详细,官方也没有更新对HEVC(H265,后文统称HEVC)tag的支持,于是在实现的过程中走了不少弯路。最终,在广大网友以及 ffmpeg 代码的帮助下,我实现了通过...
  • 手机端: EasyRTMP EasyRTMP项目 Github地址 RTMP服务器: nginx-http-flv window版(编译好) 网页端: vue + flv.js 工作流程(参考EasyRTMP工作流程图) 实现步骤 启动nginx-httpflv服务 启动命令 //解压目录下使用...
  • 简易RTMP 打开 RTMP 对象
  • 在上一篇方案《EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)》中我们介绍了将海康安防摄像机进行互联网直播的整体方案流程,其中有一个“数据处理与分析”流程我们没有详细...
  • EasyRTMP推送RTMP扩展支持HEVC(H265)

    千次阅读 2019-04-07 22:20:13
    不久前刚实现EasyRTMP扩展支持h265推送,当时在网上也查找了很多资料,发现都不尽详细,而官方也没有更新对HEVC(H265,后文统称HEVC)tag的支持,反正是走了不少弯路,当然,在广大网友以及ffmpeg代码的帮助下我最终...
  • 在上一篇方案《EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)》我们介绍了将海康安防摄像机进行互联网直播的整体方案流程,其中有一个流程“数据处理与分析”我们当时没有...
  • 方面的经验,我们推荐了海康HCNetSDK+EasyRTMP推流到RTMP流媒体服务器,再由RTMP流媒体服务器同步输出RTMP/HTTP-FLV/HLS的方案。 一般情况下我们在对接一款设备,进行流处理和流转的大概流程分为: 第一步...
  • 本文转自EasyDarwin开源团队Kim的博客:... EasyRTMP封装了RTMP协议,提供了一套非常简单易用的接口调用SDK,Github上有多个基于EasyRTMP SDK的Demo。Git地址:https://github.com/EasyDarwin/Easy
  • EasyRTMP是一套调用简单、功能完善、运行高效稳定的RTMP功能组件,经过TSINGSEE青犀视频多年实战和线上运行打造,支持RTMP推送断线重连、环形缓冲、智能丢帧、网络事件回调,能够完美应用于各种行业的直播需求,手机...
  • EasyRTMP就是一款有青犀团队打造的一款高效稳定的RTMP推流功能组件,今天为大家分享EasyRTMP新增功能,支持rtmps推送。 EasyRTMP支持rtmps推送 在使用EasyRTMP过程中,测试发现,libEasyRTMP只支持推送rtmp...
  • 最近因为公司的小区安防项目,需要通过手机来对小区内网摄像头进行实时监控,对流数据进行先拉后推,搞了两天,查了不少以资料,最后以EasyRtmp+Nginx的形式很快就实现了需求。下面简要说一下操作流程。一、工具及...
  • 本文转自EasyDarwin开源团队成员Kim的博客:http://blog.csdn.net/jinlong0603/article/details/73441405介绍EasyRTMP是EasyDarwin团队开发的一套夸平台的RTMP直播推送功能组件,内部集成了包括:基本RTMP协议、断线...
  • Real Time Messaging Protocol(RTMP)即实时消息传输协议,是 Adobe 公司开发的一个基于 TCP 的应用层协议,目前国内的视频云服务都是以 RTMP...如上代码设置完毕后,我们的EasyRTMP-Android的授权key就设置完成了。
  • EasyRTMP EasyRTMP是TSINGSEE青犀开放平台开发的一套RTMP直播推送功能组件,内部集成了包括:基本RTMP协议、断线重连、异步推送、环形缓冲区、推送网络拥塞自动丢帧、缓冲区关键帧检索、事件回调(断线、音视频数据回...
  • 中的EasyRTMP_RTSP早已经实现了在Windows/Linux上的RTSP流转RTMP流,包括了各种中间解析、断线重连的功能,本讲就是实现的Android平台的RTSP转RTMP的功能实现; 介绍 目前安防行业很多IP Camera只提供了...
  • 视频流媒体中视频数据的传输占据了绝大部分的带宽,如何提升编码效率、减小带宽使用、提升画面质量,...EasyRTMP是一套调用简单、功能完善、运行高效稳定的RTMP推流功能组件,经过多年客户实战和线上运行打造,支...
  • RTMP推流组件EasyRTMP是结合了多种音视频缓存及网络技术的一个rtmp直播推流端,包括:圆形缓冲区(circular buffer)、智能丢帧、自动重连、rtmp协议等等多种技术,能够非常有效地适应各种平台(Windows、Linux、ARM、...
  • 本文转自EasyDarwin团队Kim的博客:http://blog.csdn.net/jinlong0603/article/details/52963378EasyRTMP Android版demo中,通过使用AudioRecord录音,然后将每一帧数据通过MediaCodec编码成LATM格式音频,然后就...
  • 使用和了解EasyRTMP组件的朋友们应该清楚,手机屏幕推流时支持直播推送、屏幕推送、录像等功能,不拘泥于手机品牌与网络环境,实现课堂直播、互联网直播、手机直播等,符合现行条件下网络突飞猛进的需求。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 483
精华内容 193
关键字:

easyrtmp