客户端(RTMP系列四)

作者: onesixthree | 来源:发表于2017-07-29 17:31 被阅读75次

    客户端设计

      本项目为满足多平台监控需求,设计了Linux、Android和网页客户端。其中网页客户端基于JWplayer。Linux与Android客户端需要完成的功能主要有以下几部分:

    1. 完成与服务器的通信,接收RTMP数据;
    2. 从获取的FLV数据中提取H264 Nalu单元数据;
    3. 完成H264的解码,获取YUV数据;
    4. 将YUV数据通过SDL(Android系统通过Opengl)显示到屏幕上。

      客户端的的架构图如图1-1所示。


    客户端架构

      接收RTMP数据部分由RMTP协议模块负责,在介绍服务器与客户端通信有详细介绍,接收从服务器发送过来的FLV数据。FLV解析模块从FLV中提取Nalu单元,放入共享内存,然后调用FFmepg的解码接口从共享内存中读取H264数据解码为YUV数据,最后将YUV数据通过显示模块显示到屏幕上。

    客户端的实现

    Linux下播放器的实现

    RTMP协议模块的实现

      按照Linux客户端的设计流程,首先我们需要完成的是RTMP协议模块。客户端需要用到Librtmp来接收RTMP流数据,函数的执行流程如图1-2所示。

    接收数据流程图

      函数RTMP_Alloc()、RTMP_Init()在RTMP服务器中已经使用过,用来初始化用于RTMP通信的RMTP结构体。接下来的RTMP_Connect()、RTMP_ConnectStream()、RTMP_Read()实现和RTMP服务器建立RTMP流媒体传输的握手、连接、创建流以及播放过程。

    FLV解析模块的实现

      本项目实现的FLV解析模块设计的初衷是在包含H264数据的FLV流中提取出H264 Nalu数据。在第二章中有介绍过FLV流的结构,但是通过对FLV二进制流的分析后发现,一个video Tag数据中不仅仅包含视频数据,而且还包含访问分割符。因此,在提取Nalu数据时,需要对video tag中不同的信息进行区别。在本模块中,对非SPS、PPS、I帧和P帧的数据进行舍弃。
      首先对从RTMP传递过来的FLV Tag数据buf进行分析,如果时Tag数据包含的是视频数据就进行下一步分析,如果不是则舍弃。如果获得的是视频数据,那么对Video Tag的数据部分进行分析,首先根据前两个字符来区分关键帧和非关键帧,再保存Nalu长度,分析Nalu包的前两个字符,如果是单元分割符或者SEI则舍弃,如果是数据帧,则加上Nalu头即0x00000001,然后送入缓冲区,等待解码播放。核心代码如下:

    nalu_type=buf[naltail_pos+1]&0xff;
    if(nalu_type==1)
    {
        naltail_pos+=5;
        while(naltail_pos<nRead-5)
        {
            naltail_pos++;
            size=(buf[naltail_pos++]&0xff)*65536;
            size+=(buf[naltail_pos++]&0xff)*256;
            size+=buf[naltail_pos++]&0xff;
            nalu_type=buf[naltail_pos]&0xff;
            if(nalu_type==9||nalu_type==6)
            {
                naltail_pos+=size;
                continue;
            }
            body = (char*)malloc(size+4);
            memset(body,0,size+4);
            i=0;
            body[i++] = 0x00;
            body[i++] = 0x00;
            body[i++] = 0x00;
            body[i++] = 0x01;
            memcpy(&body[i],buf+naltail_pos,size);
            int error;
            error=decode(handler,body,size+4);
            free(body);
            naltail_pos+=size;
            break;
        }
    }
    
    解码模块的实现

      解码模块和编码模块一样,使用到了FFmpeg,和编码流程相类似,本项目的解码流程如图1-3所示。

    Ffmpeg解码流程图

    流程图中关键函数的作用:

    • av_register_all():注册所有的编解码器。
    • avcodec_find_decoder():查找解码器,这里我们的需要查找的编码器的类型为AV_CODEC_ID_H264。
    • avcodec_alloc_context3():为AVCodecContext分配内存。
    • avcodec_open2():打开解码器。
    • putdata():从缓存区循环提取数据,并调用解码函数进行解码。
    • avcodec_decode_video2():解码一帧数据(循环解码中用到的就是这个函数)。

    显示模块的实现

      Linux客户端的显示模块基于SDL实现,SDL模块主要的功能是将YUV图像显示到屏幕上面,由于是从服务器端接收数据,时间戳不用我们来设置,每接收一帧视频数据就调用解码端和显示端进行解码显示。解码和显示是连接在一起的,每解码得到YUV数据就立即通过FFmpeg函数提供的sws_scale()函数对其进行图像拉伸,以适用于显示,最后调用SDL_UpdateTexture ()函数显示到SDL开辟的View当中。
      最后首先运行RTMP服务器,然后运行Linux客户端,播放监控数据,运行如图1-4所示。

    Linux客户端

    相关文章

      网友评论

        本文标题:客户端(RTMP系列四)

        本文链接:https://www.haomeiwen.com/subject/tyjclxtx.html