zoukankan      html  css  js  c++  java
  • 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(五)用户接口层之提取媒体流数据

    当RTSP客户端向RTSP服务端发送完PLAY命令后,RTSP服务端就会另外开启UDP端口(SDP协商定义的端口)发送RTP媒体流数据包。这些数据包之间会间隔一段时间(毫秒级)陆续被发送到RTSP客户端,此时RTSP客户端可以调用GetMediaData等接口获取媒体流数据。

    一、uint8_t * RtspClient::GetMediaData(string media_type, uint8_t * buf, size_t * size, size_t max_size)

    该函数的作用即获取媒体流数据,并将数据放入参数buf中,数据大小放入size中,media_type可以为字符串“audio”或“video”,max_size为buf的最大值。

    if(it->second.MediaType == "video") return GetVideoData(&(it->second), buf, size, max_size);
    if(it->second.MediaType == "audio") return GetAudioData(&(it->second), buf, size, max_size);

    该函数首先在MediaSessionMap中查询匹配media_type的媒体会话,然后选择调用GetVideoData或GetAudioData。

     1 uint8_t * RtspClient::GetMediaData(string media_type, uint8_t * buf, size_t * size, size_t max_size) 
     2 {
     3         MyRegex Regex;
     4         map<string, MediaSession>::iterator it;
     5         bool IgnoreCase = true;
     6         if(!buf) return NULL;
     7         if(!size) return NULL;
     8 
     9         *size = 0; 
    10 
    11         for(it = MediaSessionMap->begin(); it != MediaSessionMap->end(); it++) {
    12                 if(Regex.Regex(it->first.c_str(), media_type.c_str(), IgnoreCase)) break;
    13         }
    14 
    15         if(it == MediaSessionMap->end()) {
    16                 fprintf(stderr, "%s: No such media session
    ", __func__);
    17                 return NULL;
    18         }
    19 
    20         if(it->second.MediaType == "video") return GetVideoData(&(it->second), buf, size, max_size);
    21         if(it->second.MediaType == "audio") return GetAudioData(&(it->second), buf, size, max_size);
    22         return NULL;
    23 }
    View Code

      

    二、uint8_t * RtspClient::GetVideoData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size, bool get_vps_sps_pps_periodly)

    if(true == get_vps_sps_pps_periodly) {
    if(GetVideoDataCount >= GetSPS_PPS_Period) {
    GetVideoDataCount = 0;

    ......

    }

    参数get_vps_sps_pps_periodly用来指示是否要周期性写入VPS、SPS和PPS(h264/h265中参数),默认为true。周期写入的目的是为了防止视频传输一开始将这些关键参数丢失,虽然会多耗费一点带宽,但是相对视频数据本身可谓九牛一毛,重要的是这样可以用来防止由于这些参数丢失所导致的诸如花屏之类的问题。

    if(media_session->EncodeType == "H264") {
    NALUTypeBaseTmp = &NaluBaseType_H264Obj;
    } else if (media_session->EncodeType == "H265") {
    NALUTypeBaseTmp = &NaluBaseType_H265Obj;
    } else {
    // Unknown Nalu type
    printf("Unsupported codec type: %s ", media_session->EncodeType.c_str());
    return NULL;
    }

    然后根据media_session中的编码类型,获取RTP音视频传输解析层中的特定类对象(NaluBaseType_H264Obj、NaluBaseType_H265Obj)来处理视频数据。

    do {
    EndFlag = true;

    ......

    EndFlag = NALUType->GetEndFlag();
    } while(!EndFlag);

    接着是循环获取视频帧的NALU单元,由于一个NALU单元常常会超过一个MTU(最大传输单元),所以NALU单元就会被分包,最后一个分包会带一个结束标志,如果获得了NALU单元的最后一个分包,则跳出该循环并从函数中返回。

     1 uint8_t * RtspClient::GetVideoData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size, bool get_vps_sps_pps_periodly) 
     2 {
     3     if(!media_session || !buf || !size) return NULL;
     4 
     5     *size = 0;
     6 
     7     const size_t GetSPS_PPS_Period = GET_SPS_PPS_PERIOD; // 30 times
     8     if(true == get_vps_sps_pps_periodly) {
     9         if(GetVideoDataCount >= GetSPS_PPS_Period) {
    10             GetVideoDataCount = 0;
    11 
    12             const size_t NALU_StartCodeSize = 4;
    13             size_t SizeTmp = 0;
    14             if(!GetVPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
    15                 // fprintf(stderr, "33[31mWARNING: No H264 VPS33[0m
    ");
    16             } else {
    17                 *size += SizeTmp;
    18             }
    19             if(!GetSPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
    20                 fprintf(stderr, "33[31mWARNING: No SPS33[0m
    ");
    21             } else {
    22                 *size += SizeTmp;
    23             }
    24             if(!GetPPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
    25                 fprintf(stderr, "33[31mWARNING: No PPS33[0m
    ");
    26             } else {
    27                 *size += SizeTmp;
    28             }
    29             return buf;
    30         } else {
    31             GetVideoDataCount++;
    32         }
    33     }
    34 
    35     size_t SizeTmp = 0;
    36     bool EndFlag = false;
    37     NALUTypeBase * NALUTypeBaseTmp = NULL;
    38     NALUTypeBase * NALUType;
    39 
    40     int PM = media_session->Packetization;
    41     if(!IS_PACKET_MODE_VALID(PM)) {
    42         cerr << "WARNING:Invalid Packetization Mode" << endl;
    43         return NULL;
    44     }
    45     if(media_session->EncodeType == "H264") {
    46         NALUTypeBaseTmp = &NaluBaseType_H264Obj;
    47     } else if (media_session->EncodeType == "H265") {
    48         NALUTypeBaseTmp = &NaluBaseType_H265Obj;
    49     } else {
    50         // Unknown Nalu type
    51         printf("Unsupported codec type: %s
    ", media_session->EncodeType.c_str());
    52         return NULL;
    53     }
    54 
    55     do {
    56         EndFlag = true;
    57         if(!media_session->GetMediaData(VideoBuffer.Buf, &SizeTmp)) return NULL;
    58         if(0 == SizeTmp) {
    59             cerr << "No RTP data" << endl;
    60             return NULL;
    61         }
    62         int NT; 
    63         NT = NALUTypeBaseTmp->ParseNALUHeader_Type(VideoBuffer.Buf);
    64         NALUType = NALUTypeBaseTmp->GetNaluRtpType(PM, NT);
    65         if(NULL == NALUType) {
    66             printf("Unknown NALU Type: %s
    ", media_session->EncodeType.c_str());
    67             return NULL;
    68         }
    69 
    70         if(SizeTmp > VideoBuffer.Size) {
    71             cerr << "Error: RTP Packet too large(" << SizeTmp << " bytes > " << VideoBuffer.Size << "bytes)" << endl;
    72             return NULL;
    73         }
    74 
    75         if(*size + SizeTmp > max_size) {
    76             fprintf(stderr, "33[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)33[0m
    ", *size + SizeTmp, max_size);
    77             return buf;
    78         }
    79 
    80         SizeTmp = NALUType->CopyData(buf + (*size), VideoBuffer.Buf, SizeTmp);
    81         *size += SizeTmp;
    82         EndFlag = NALUType->GetEndFlag();
    83     } while(!EndFlag);
    84 
    85     return buf;
    86 }
    View Code

    三、uint8_t * RtspClient::GetAudioData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size)

    该函数和GetVideoData的思路一样:

    先是获取RTP音视频传输解析层中的特定类对象(MPEG_AudioObj),然后再去获取音频数据流。

     1 uint8_t * RtspClient::GetAudioData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size)
     2 {
     3         if(!media_session || !buf || !size) return NULL;
     4 
     5         *size = 0;
     6 
     7         size_t SizeTmp = 0;
     8         MPEGTypeBase * MPEGType;
     9 
    10         if(!media_session->GetMediaData(AudioBuffer.Buf, &SizeTmp)) return NULL;
    11         if(0 == SizeTmp) {
    12                 cerr << "No RTP data" << endl;
    13                 return NULL;
    14         }
    15 
    16         MPEGType = &MPEG_AudioObj;
    17 
    18         if(SizeTmp > AudioBuffer.Size) {
    19                 cerr << "Error: RTP Packet too large(" << SizeTmp << " bytes > " << AudioBuffer.Size << "bytes)" << endl;
    20                 return NULL;
    21         }
    22 
    23         if(*size + SizeTmp > max_size) {
    24                 fprintf(stderr, "33[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)33[0m
    ", *size + SizeTmp, max_size);
    25                 return buf;
    26         }
    27 
    28         SizeTmp = MPEGType->CopyData(buf + (*size), AudioBuffer.Buf, SizeTmp);
    29         *size += SizeTmp;
    30 
    31         return buf;
    32 }
    View Code

    上一篇                       回目录                    下一篇

  • 相关阅读:
    第二十九天- socketserver模块 ftp上传
    第二十八天- tcp下的粘包和解决方案
    记录每次更新到仓库
    Python在自动化运维时经常会用到的方法
    运维新手们,别再问需不需要学PYTHON了!!!
    nagios监控系统搭建!!
    Nagios:企业级系统监控方案
    打造自己的Cacti模板
    Cacti插件
    Cacti脚本及模板
  • 原文地址:https://www.cnblogs.com/ansersion/p/7425163.html
Copyright © 2011-2022 走看看