王学岗—————————哔哩哔哩直播-手写哔哩哔哩硬编码录屏推流(硬编)(26节课,27节课,28节课,29节课)

RTMP简介

1,直播也分为硬编码与软编码,硬编码包括camerax等。
2,从摄像头得到数据不能直接推到直播服务器,需要编码成h264或h265(h265需要服务器支持)。把编码好的数据发送到直播服务器。客户端会连接直播服务器,从直播服务器拉取数据。
3,影响数据量最大的因素是帧率。音视频直播帧率是很低的。以腾讯直播为例,只有15帧。
4,直播秒开率:第一,减少I帧的间隔时间;第二,降低帧率(帧率Frame rate是以帧称为单位的位图图像连续出现在显示器上的频率(速率));一般直播帧率都很低,比如腾讯直播fps只有15。第三:GOP(两个I帧之间)缓存, 服务端缓存一个最新的GOP,服务端可以有两个数据,缓存的GOP,播放的GOP。
5,GOP影响seek速度,加载速度,缓存速度
在这里插入图片描述

在这里插入图片描述
图sps+pps数据注:第六个字节是版本号。第七八九个字节,取sp帧前面三个字符(分别表示编码等级、约束、和最大支持码流范围),
第十个是一个字节写法是固定的。后面两个字节表示sps长度(每个视频sps长度未必一样),后面就是sps内容(前面已经告诉了服务器sps的长度),再往后pps个数是固定0x01;接着是pps长度和pps内容
4,
如果我们把分隔符(0x00 00 01)之间的数据推给服务器,这种方式虽然行,但有问题。如果服务器想知道你的帧大小是多少,服务器必须解析到下一个分隔符才行。这种方式对于直播服务器来说消耗太大。如果服务器想知道I帧,p帧,b帧的大小,通过H264自己的I帧,p帧,b帧机制,需要一个个的去轮询,轮询到下一个分隔符。如果服务器想知道帧的时间戳,就需要把帧解析出来。这样太消耗服务器性能了。

这样我们就设置了RTMP协议。RTMP协议,帮助服务端更好的分发给各个客户端,并且服务器不需要解码h264(h264解码非常消耗资源)。就能够获取绝大部分需要的信息。RTMP是用来推流的,从直播推到服务器。
5,RTMP有三种帧类型:1,sps/pps配置帧;2,I帧;3,b帧和p帧。

6,服务器如何区别帧类型呢?根据第一个字节进行区分。关键帧和sps/pps第一个字节是0x17,服务端会判断是I帧或sps/pps。因为第一个字节无法区分关键帧和sps/pps帧,需要借助第二个字节。关键帧的第二个字节是0x01,sps/pps第二个字节是0x00。
7,4字节数据长度(int类型就是占用四个字节):四个字节存放帧的长度,服务器读取这四个字节就可以,不需要读到下一个分割符才知道I/b/p帧的长度。最后就是帧的裸数据了。这样I/p/b帧就传到服务器了。
8,非关键帧(b/p)与关键帧(I)传输同理.
9,sps与pps。因为长度小,存储长度只需要两个字节。版本是rtmp的版本。详见上图下半部分说明。

10,RTMP不解析内容只是方便转发。客户端接收到的数据(从服务器来)不是我们推的数据,而是标准的h264。
11,回顾sps
在这里插入图片描述

64代表编码等级。00代表
在这里插入图片描述
64

15
在这里插入图片描述

代码(26节课)

1,rtmp是 native工程。服务器不需要自己搭建,我们用哔哩哔哩的直播服务器。数据源就用录屏的。
2,录屏代码略,前面有录屏代码。
3,我们不可能去手写RTMP协议。联想http协议,我们用httpurlconnection去添加请求头,请求方式等。Rtmpdump就是Rtmp协议的httpurlconnection。
4,Rtmpdump最新的版本是2010年。地址
我们下载最新的
在这里插入图片描述

5,medcodec录制的时候,sps/pps只会编码一次。编码的内容并不是要发送的内容(编码只编码一次,但是发送却每隔几秒就要发送一次)。要经过处理。
6,我们架构分为编码层和传输层,编码层编码出来的h264数据包直接丢给传输层会出现两种情况,编码快,发送慢,造成卡顿。编码慢发送快造成网络资源浪费。
因为编码层与传输层的速率不一致,所以我们使用队列,也就是生产者模式和消费者模式,编码出来的数据我们丢给队列。传输层从队列取数据。
在这里插入图片描述

这是我们的数据包

package com.maniu.rtmpbibili;

public class RTMPPackage {
//    帧数据
    private byte[] buffer;
//    时间戳
    private long tms;

    public RTMPPackage(byte[] buffer, long tms) {
        this.buffer = buffer;
        this.tms = tms;
    }

    public byte[] getBuffer() {
        return buffer;
    }

    public void setBuffer(byte[] buffer) {
        this.buffer = buffer;
    }

    public long getTms() {
        return tms;
    }

    public void setTms(long tms) {
        this.tms = tms;
    }

}

哔哩哔哩直播服务器地址

在这里插入图片描述

传输层ScreenLive。

native层
在这里插入图片描述
把下载的压缩包解压,把librtmp放到cpp里
在librtmp中 创建CMAKELIST.txt文件

#关闭ssl 不支持rtmps  rtmp   加密  传递一变量  进制 加密验证
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_CRYPTO"  )
file(GLOB rtmp_source  *.c)#要编译的文件,把.c文件赋值给rtmp_source变量。GLOB代表定义全局变量
#将remp_source的代码 生成 rtmp 静态库
add_library(rtmp123
        STATIC
        ${rtmp_source})

在系统创建的CMAKELIST中引用我们自己创建的


cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(librtmp)#添加整个目录,在这里查找rtmp
add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             native-lib.cpp )
 #从系统目录中查找叫log的库,查找后把log赋值给log-lib中的变量
find_library( # Sets the name of the path variable.
              log-lib
              log )

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib}
        rtmp123)#因为已经添加了目录,所以这里可以查找到我们自己创建的。

API使用流程
在这里插入图片描述
代码中使用

#include <jni.h>
#include <string>
extern "C"
{
    #include  "librtmp/rtmp.h"
}
#include <android/log.h>
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"David",__VA_ARGS__)

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_maniu_rtmpmaniu_ScreenLive_connect(JNIEnv *env, jobject thiz, jstring url_) {

    const char *url = env->GetStringUTFChars(url_, 0);
    int ret;
//实例化对象,返回RTMP对象
    RTMP * rtmp=RTMP_Alloc();
	//初始化RTMP对象
     RTMP_Init(rtmp);
	 //设置超时时间
    rtmp->Link.timeout = 10;
	//设置URL地址
    ret =RTMP_SetupURL(rtmp, (char*)url);
	//0代表失败(false),这里可以打印看下是否成功
    if (ret == TRUE) {
        LOGI("RTMP_SetupURL");
    }
	//开启可写
    RTMP_EnableWrite(rtmp);
    LOGI("RTMP_EnableWrite");
    //连接数据流
    ret = RTMP_Connect(rtmp, 0);
    if (ret == TRUE) {
        LOGI("RTMP_Connect ");
    }
     ret = RTMP_ConnectStream(rtmp, 0);
    if (ret == TRUE) {
        LOGI("connect success");
    }
    env->ReleaseStringUTFChars(url_, url);
    return ret;
}

sps 与pps的内容的算法
在这里插入图片描述

音频

在这里插入图片描述

native层找bug

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值