同屏服务端源码分析日志(RTSP推流接收端)

一、RTSP推流服务器端概述

RTSP是一种网络控制协议,用于控制实时数据的传输,如音频和视频。它允许客户端与流媒体服务器进行交互,以控制和接收实时的音视频流。RTSP的设计目标是提供一种标准化的协议,使得音视频流能够在客户端和服务器之间进行控制和传输。

二、处理步骤

当RTSP推流服务器端接收到流后,会进行以下处理步骤:

  1. 连接管理:
    服务器会监听特定的端口(如554端口),等待客户端的连接请求。
    当接收到客户端的连接请求时,服务器会建立TCP连接,并初始化RTSP会话。
  2. 指令区分:
    服务器会解析客户端发送的RTSP请求报文,区分出控制指令和数据指令。
    控制指令包括DESCRIBE、SETUP、PLAY、PAUSE、TEARDOWN等,用于控制媒体流的播放、暂停、停止等操作。
    数据指令则通常与RTP/RTCP协议相关,用于传输实际的音视频数据。
  3. RTP/RTCP处理:
    对于数据指令,服务器会进一步区分出RTP和RTCP报文。
    RTP报文用于传输音视频数据,而RTCP报文则用于传输控制信息,如发送者报告和接收者报告。
  4. 音视频数据处理:
    对于RTP报文中的音视频数据,服务器会进行解码和重组。
    视频数据需要按照序列号进行排序,以处理可能出现的乱序情况。
    排序后的视频数据会进行解码,并合成视频帧以供播放或存储。
  5. 反馈与质量控制:
    服务器会利用RTCP报文中的发送者报告和接收者报告来监控音视频流的质量。
    根据这些报告,服务器可以调整传输速率、编码方式等参数,以优化音视频流的传输效果。

三、指令与数据区分

在RTSP推流过程中,服务器需要区分RTSP控制指令和数据指令。这通常通过解析请求报文的首部行和实体主体来实现。

  • 控制指令:RTSP请求报文的首部行会包含方法名称(如DESCRIBE、SETUP等)和URL地址等信息。服务器会根据这些信息来识别并处理控制指令。
  • 数据指令:与RTP/RTCP协议相关的数据指令则通常通过特定的端口和传输协议进行传输。服务器会监听这些端口,并解析接收到的RTP/RTCP报文来处理音视频数据和控制信息。

四、乱序处理与视频帧合成

对于RTP报文中的音视频数据,服务器需要处理可能出现的乱序情况。这通常通过以下方式实现:

  • 序列号排序:服务器会按照RTP报文中的序列号对音视频数据进行排序。序列号是一个递增的整数,用于标识每个数据包的顺序。
  • 缓冲与重组:排序后的音视频数据会被放入缓冲区中进行重组。对于视频数据,服务器会根据帧头和帧尾信息来识别并提取完整的视频帧。
  • 解码与播放:重组后的视频帧会进行解码处理,并转换为可供播放或存储的格式。解码后的视频帧可以被发送到播放器进行播放,或者存储到文件系统中以供后续使用。

五、总结

RTSP推流服务器端在接收到流后,会进行连接管理、指令区分、RTP/RTCP处理、音视频数据处理以及反馈与质量控制等步骤。在处理过程中,服务器需要区分RTSP控制指令和数据指令,并对音视频数据进行排序、缓冲、重组和解码处理。对于视频数据,服务器还需要进行视频帧的合成和播放控制等操作。这些处理步骤共同确保了音视频流的实时传输和高质量播放。

六、项目介绍

整个同屏流程是基于jmdns发现协议和rtsp推流实现,本文分析同屏服务端app的源码。

  1. jmdns服务注册,使得该服务能够被网络上的其他设备通过mDNS协议发现
1.MainActivity.java -> initViews()
BaseApp.getInstance().getAirPlayerController().init(this);

2.ExplayerController.java -> init(Context context)
BoxService.startService(context,deviceName);

3.BoxService.java -> startService(Context context,String deviceName)
Intent intent = new Intent(context, BoxService.class);
context.getApplicationContext().startService(intent);

-> onCreate()
boxServer = new ExBoxServer(deviceName);
boxServer.start();

4.ExBoxServer.java -> run()
mBonjour = new BonjourExplay(name, port);
mBonjour.start();

5.Bonjour.java -> start()
mHandler.obtainMessage(MSG_START).sendToTarget();

-> internalRefresh()
JmDNS dns = mAddr2Dns.get(addr);
if (dns == null) {
  ServiceInfo info =  onCreateServiceInfo(addr.getAddress(), name, port);
  dns= JmDNS.create(addr);
  dns.registerService(info);
  mAddr2Dns.put(addr, dns);
}
  1. 初始化rtsp接收服务端,并设置音频和视频帧接收回调
1.MainActivity.java -> initViews()
PlayerManager.getInstance().init(context);

2.PlayerManager.java -> init(Context context)
loadLibrariesOnce();

->loadLibrariesOnce()
System.loadLibrary("media_server");

3.native-lib.cpp -> JNI_OnLoad(JavaVM *vm, void * /* reserved */)
cb.Mcast_Audio_Init = onNotifyMirrorAudioCodecInfo;
cb.Mcast_Audio_Process = onMirrorAudioData;
cb.Mcast_Audio_destroy = onStopAudioPlayer;
cb.Mcast_Video_Play = onStartMirrorPlayer;
cb.Mcast_Video_Process = onMirrorVideoData;
cb.Mcast_Video_Stop = onStopMirrorPlayer;
startRtspServer(&cb);

4.RtspServer.cpp -> startRtspServer(mcast_callbacks_t *cb)
mk_rtsp_server_start(8554, 0);
mk_events events = {
  .on_mk_media_changed = on_mk_media_changed,
  .on_mk_media_publish = on_mk_media_publish,
  .on_mk_media_play = on_mk_media_play,
  .on_mk_media_not_found = on_mk_media_not_found,
  .on_mk_media_no_reader = on_mk_media_no_reader,
  .on_mk_http_request = on_mk_http_request,
  .on_mk_http_access = on_mk_http_access,
  .on_mk_http_before_access = on_mk_http_before_access,
  .on_mk_rtsp_get_realm = on_mk_rtsp_get_realm,
  .on_mk_rtsp_auth = on_mk_rtsp_auth,
  .on_mk_record_mp4 = on_mk_record_mp4,
  .on_mk_shell_login = on_mk_shell_login,
  .on_mk_flow_report = on_mk_flow_report
};
mk_events_listen(&events);

3.视频帧接收跟踪分析

1.RtpSession.cpp -> onRecv(const Buffer::Ptr &data)
收到推流数据
RtpSplitter::input(data->data(), data->size());


2.HttpRequestSplitter.cpp -> input(const char *data,size_t len)
_content_len = onRecvHeader(header_ptr, header_size);

3.RtspSplitter.cpp -> onRecvHeader(const char *data, size_t len)
分离出rtcp(与RTP紧密配合,共同实现流媒体传输的监控和管理)、rtp音视频数据和rtsp指令数据,将rtcp,rtp数据进行下一步处理
 onRtpPacket(data, len);

4.RtspSession.cpp -> onRtpPacket(const char *data, size_t len)
handleOneRtp(track_idx, _sdp_track[track_idx]->_type, _sdp_track[track_idx]->_samplerate, (uint8_t *) data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
继续分离rtcp和rtp数据,将rtp数据进行下一步处理

5.RtpReceiver.h -> handleOneRtp(int index, TrackType type, int sample_rate, uint8_t *ptr, size_t len)
_track[index].inputRtp(type, sample_rate, ptr, len).operator bool()

6.RtpReceiver.cpp -> inputRtp(TrackType type, int sample_rate, uint8_t *ptr, size_t len)
sortPacket(rtp->getSeq(), rtp);

7.RtpReceiver.h -> sortPacket(SEQ seq, T packet)
该方法进行数据包排序后再继续下一步处理。由于数据包可能不会按顺序到达,这里判断数据包的序列号是否是连续,不是连续就缓存起来,直到接收到正确的数据包再输出到下一步,如果一直收不到就输出缓存的数据包,并更新序列号判断。
->output(SEQ seq, T packet)
->_cb(seq, std::move(packet));

8.RtpReceiver.cpp -> setOnSort([this](uint16_t seq, RtpPacket::Ptr packet)
onRtpSorted(std::move(packet));
->onRtpSorted(RtpPacket::Ptr rtp)
_on_sorted(std::move(rtp));

9.RtpReceiver.h -> RtpMultiReceiver()
onRtpSorted(std::move(rtp), index);

10.RtspSession.cpp -> onRtpSorted(RtpPacket::Ptr rtp, int track_idx)
_push_src->onWrite(std::move(rtp), false);

11.RtspMediaSourceImp.cpp -> onWrite(RtpPacket::Ptr rtp, bool keyPos)
PacketCache<RtpPacket>::inputPacket(stamp, is_video, std::move(rtp), keyPos);

12.RtspDemuxer.cpp -> RtspDemuxer::inputRtp(const RtpPacket::Ptr &rtp)
_video_rtp_decoder->inputRtp(rtp, true);

13.H264Rtp.cpp -> H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos)
decodeRtp(rtp);
->H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp)
mergeFu(rtp, frame, payload_size, stamp, seq)
->H264RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssize_t size, uint64_t stamp, uint16_t seq)
该方法进行数据包合并帧的动作,由于单个视频帧数据会大于单个数据包,所以需要将多个数据包合并成一个完整的视频帧再进行下一步操作
outputFrame(rtp, _frame);
->H264RtpDecoder::outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr &frame)
RtpCodec::inputFrame(frame);

14.Frame.h -> inputFrame(const Frame::Ptr &frame)
pr.second->inputFrame(frame)

15. RtspServer.cpp -> on_mk_video_frame_out(void *user_data, mk_frame frame)
 rtsp_cb->Mcast_Video_Process(playerId, (uint8_t*)mk_frame_get_data(frame), mk_frame_get_data_size(frame),
                                 mk_frame_get_pts(frame), 0, 1920, 1080);

16.native-lib.cpp -> onMirrorVideoData(uint32_t playerId, const uint8_t *p_src, uint32_t size, int64_t ptsValue,int rota,int width,int height)
ret = env->CallStaticIntMethod(g_miracastNativeMethod.id,
                                   g_miracastNativeMethod.method_onMirrorVideoData,
                                   playerId, jbarray, size, ptsValue,rota,width,height);

17.ExPlayerManager.java -> onMirrorVideoData(int playerId, byte[] p_src, int size, long ptsValue)
player.onEventListener.onMirrorVideoData(player, p_src, size, ptsValue);

18.MainActivity.java -> onMirrorVideoData(ExPlayer player, byte[] p_src, int size, long ptsValue)
videoView.getVideoPlayer().putEncodeFrame(p_src, 0, size, ptsValue);
视频帧数据加入队列中,后面另开子线程取数据进行播放



©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容