从零开始成为GStreamer专家——RTSP播放开发

这篇具有很好参考价值的文章主要介绍了从零开始成为GStreamer专家——RTSP播放开发。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

从零开始成为GStreamer专家——RTSP播放开发

        RTSP不同于本地播放,也不同于http,它需要动态创建source的srcpad,不可以直接将source和typefind相连,涉及到的元素按创建时间的先后顺序有:

rtspsrc

rtspwms

rtspreal

udpsrc

udpsrc

rtpbin

rtpsession

rtpssrcdemux

rtpstorage

udpsink

fakesrc

udpsink

rtpjitterbuffer

rtpptdemux

层次关系如下:

gst_rtsp_extension_send,音视频,实时音视频,gstreamer

        数据流向:GstUdpSrc->GstRtpSession->GstRtpStorge->GstRtpSsrcDemux->GstRtpJitterBuffer->GstRtpDemux->GstTypeFindElement

UdpSrc可能有多个线程:gst_rtsp_extension_send,音视频,实时音视频,gstreamer

         UdpSrc用gst_base_src_get_range来获取buffer数据并且产生buffer的pts,dts。gst_base_src_do_sync函数产生buffer的pts。注意,如果basesrc没有clock,那么,pts和dts则不会正确产生出来,为-1。PTS,DTS异常的buffer,被投递到RtpJitterBuffer之后,会被丢弃,产生错误,因此,需要为basesrc配置时钟,就是配置元素的clock。

        RTSP调用gst_rtspsrc_stream_configure_udp函数,在UdpSrc加放到RTSP这个bin当中时,由gstbin的gst_bin_add_func函数配置clock,这个clock也就是RTSP bin的一个clock。注意,RTSP这个sorce是一个bin。而RTSP bin的时钟是由pipeline提供的,在GST_STATE_CHANGE_PAUSED_TO_PLAYING状态切换时,gst_pipeline_change_state函数用gst_element_provide_clock产生一个clock,也就是说,clock得等到playing状态时才有。RTSP的动态pad得在playing状态产生。

gst_rtsp_extension_send,音视频,实时音视频,gstreamer

 和typefind相连的动态产生的source,这个source的pad开放出去流程如下:

source_new_pad subprojects\gst-plugins-base\gst\playback\gsturidecodebin.c(2170)
……
g_signal_emit	                subprojects\glib\gobject\gsignal.c(3555)
gst_element_add_pad	            subprojects\gstreamer\gst\gstelement.c(803)
new_manager_pad	                subprojects\gst-plugins-good\gst\rtsp\gstrtspsrc.c(3535)
……
g_signal_emit	                subprojects\glib\gobject\gsignal.c(3555)
gst_element_add_pad	            subprojects\gstreamer\gst\gstelement.c(803)
expose_recv_src_pad             subprojects\gst-plugins-good\gst\rtpmanager\gstrtpbin.c(3724)
……
g_signal_emit                   subprojects\glib\gobject\gsignal.c(3555)
gst_rtp_pt_demux_chain          subprojects\gst-plugins-good\gst\rtpmanager\gstrtpptdemux.c(522)
gst_pad_chain_data_unchecked	subprojects\gstreamer\gst\gstpad.c(4449)
gst_pad_push_data	            subprojects\gstreamer\gst\gstpad.c(4714)
gst_pad_push	                subprojects\gstreamer\gst\gstpad.c(4832)
pop_and_push_next               subprojects\gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(3606)
gst_rtp_jitter_buffer_loop      subprojects\gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(4222)
gst_task_func	                subprojects\gstreamer\gst\gsttask.c(384)

        可以提前手动创建一个clock来满足时钟需求,而不用全部转入到PLAYING状态。如下:

static GstElement *
setup_jitterbuffer (gint num_buffers)
{
    ......
    jitterbuffer = gst_check_setup_element ("rtpjitterbuffer");
    /* we need a clock here */
    clock = gst_system_clock_obtain ();
    gst_element_set_clock (jitterbuffer, clock);
    gst_object_unref (clock);
    ......
}
或者这样配置:
static void
setup_rtpdtmfsrc (void)
{
    ......
    testclock = gst_test_clock_new ();
    dtmfsrc = gst_check_setup_element ("rtpdtmfsrc");
    fail_unless (gst_element_set_clock (dtmfsrc, testclock));
    ......
}

        这个clock并不能完全满足播放需求,由于RTP时钟和产生时钟的巨大差异,会导致某些影片提前退出播放,具体原因在后面描述,此处贴出解决方案:

void
set_element_clock(GstElement *element)
{                    /* we need a clock here for live play */
    GstClock *clock = gst_element_provide_clock(element);
    if (!clock) {
        clock = gst_system_clock_obtain();
    }
    /* Config pipeline 4s delay */
    GstClockTime delay = 4 * GST_SECOND;
    
    GstClockTime now, start_time;
    now = gst_clock_get_time(clock);
    g_return_if_fail(gst_element_set_clock(element, clock));

    start_time = GST_ELEMENT_START_TIME(element);

    if (start_time != GST_CLOCK_TIME_NONE && now != GST_CLOCK_TIME_NONE) {
        GstClockTime new_base_time = now - start_time + delay;
        gst_element_set_base_time(element, new_base_time);
    }
}

        动态PAD产生出来之后,就可以创建PIPELINE播放节目了,typefind输出与通常直接的码流播放还不一样,这里面的输出包含了RTP的一些头信息,所以在typefind之后需要接上depayloader,然后再与demux相连。需要特别提一下的是,有的工具可通是将音频与视频分开推流的,也就是说动态产生的source可能不止一个,可能是两个或者多个(A/V/S),这时,每个source都需要连接一个typefind,然后deplayloader,demux。下图给一个3路source的示例:gst_rtsp_extension_send,音视频,实时音视频,gstreamer

gst_rtsp_extension_send,音视频,实时音视频,gstreamer

gst_rtsp_extension_send,音视频,实时音视频,gstreamer

RTSP EOS事件

        RTSP有个timer,会发送EOS事件,rtp_timer_queue_set_eos函数会将timer的seqnum设置成-1,所以timer对应的事件很快就会发生。RTP_TIMER_EOS timer出现的时候,会产生GST_EVENT_EOS事件结束播放,rtp eos timer产生流程如下:
1. 设置Timer

rtp_timer_queue_set_timer       gst-plugins-good\gst\rtpmanager\rtptimerqueue.c(608)
rtp_timer_queue_set_eos           gst-plugins-good\gst\rtpmanager\rtptimerqueue.c(664)
update_estimated_eos           gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(3493)
pop_and_push_next               gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(3554)
gst_rtp_jitter_buffer_loop       gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(4223)

来看一下关键函数,术语相关的信息可通参考
https://www.rfc-editor.org/rfc/rfc2326
https://www.ietf.org/rfc/rfc2326.txt
https://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/rtp/README
这些网站,有一些变量需要解释一下:
 /* 1. Normal play time (NPT),表示当前码流与播放开始的相对时间,比如世界杯已经播出了35分钟,观众才接入,那么当前流相对与播放开始时间就是35分钟。快快退时,这个时间相应变快,有now,range,时分秒等参数。是播放时,客户端给服务端的响应,如Range: npt=73.4-4311。
  * 2. rtptime 可以理解成收到第一个RTP包与此次会话开始的相对时间,比如会话开始在0秒,收到第一个RTP包是在10秒,第一个包的rtptime就是10。ext_timestamp就是上一个RTP包的rtptime, 由于rtptime是32位容易反转,而ext_timestamp是64位,是用来处理时间戳翻转,保证时间的有效性。初始为-1。
  * 3. clock_base就是第一个RTP包对应的rtptime,用来记录服务器已经发送了多长时间数据,可能为-1。
  * 4. clock_rate 用来转换rtptime和GStreamer time,以确定buffer中的数据量。
  * 5. GStreamer时间戳和RTP timestamp 转换关系如下:   
    RTP = ((RT - RT-base) * clock-rate / GST_SECOND) + RTP-offset
    RTP       : 流的32bits的RTP时间戳
    RT        : RTP包时对应的GStreamer时间。
    RT-base   : 第一个RTP包的GStreamer时间。
    clock-rate: the clock-rate of the stream
    RTP-offset: a random RTP offset
    clock-base就是RT为RT-base时的RTP时间戳,原文:The RTP timestamp corresponding to RT-base is the clock-base.
*/
举列某CAPS如下:

application/x-rtp, media=(string)video, payload=(int)96, clock-rate=(int)90000, encoding-name=(string)H264, packetization-mode=(string)1, profile-level-id=(string)64001F, sprop-parameter-sets=(string)"Z2QAH6w04sC0EW/8AFgAPEAAAAMAQAAADKPGDGLg\,aO68sA\=\=", a-tool=(string)"LIVE555\ Streaming\ Media\ v2014.07.18", a-type=(string)broadcast, x-qt-text-nam=(string)"Matroska\ video+audio+\(optional\)subtitles\,\ streamed\ by\ the\ LIVE555\ Media\ Server", x-qt-text-inf=(string)007_X264_6audio_5sub.mkv, clock-base=(uint)1121939676, seqnum-base=(uint)50933, npt-start=(guint64)0, npt-stop=(guint64)4807600000000, play-speed=(double)1, play-scale=(double)1, onvif-mode=(boolean)false, ssrc=(uint)948598643
ext SR 1122694431, base -1,         clock-rate 90000, clock-base 1121939676, last-rtptime -1
ext SR 1122694431, base 1121939676, clock-rate 90000, clock-base 1121939676, last-rtptime 1121939676
ext SR 1123026880, base 1121939676, clock-rate 90000, clock-base 1121939676, last-rtptime 1121939676

其中,npt-start=(guint64)0, npt-stop就是这个MKV文件的时长,clock-base取的是第一个rtptime的时间。
又如:

ext SR 247494882, base -1,        clock-rate 90000, clock-base -1, last-rtptime -1
ext SR 247494882, base 246437702, clock-rate 90000, clock-base -1, last-rtptime 246437702
ext SR 247947492, base 246437702, clock-rate 90000, clock-base -1, last-rtptime 246437702
ext SR 248397942, base 246437702, clock-rate 90000, clock-base -1, last-rtptime 246437702
ext SR 248848842, base 246437702, clock-rate 90000, clock-base -1, last-rtptime 246437702
static void
update_estimated_eos (GstRtpJitterBuffer * jitterbuffer,
    RTPJitterBufferItem * item)
{
……
  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
  /* npt_stop是流的结束时间,npt_start是流的开始时间,以下参数分别表示上一个RTP包的时间戳,第一个RTP包,以及时钟频率都没有,表明未收到流的信息及RTP包,返回。 */
  if (priv->npt_stop == -1 || priv->ext_timestamp == -1
      || priv->clock_base == -1 || priv->clock_rate <= 0)
    return;

  /* compute the elapsed time,计算的是RTP从session建立以来已经经历了多长时间,elapsed = ext_time - priv->clock_base,gst_rtp_buffer_ext_timestamp 用于获取RTP扩展时间戳(timestamp),扩展时间戳通常用于指示媒体流中的数据包的绝对时间(而非相对时间戳),以便更准确地进行同步和回放。 */
  elapsed = compute_elapsed (jitterbuffer, item);

  /* do nothing if elapsed time doesn't increment,3个RTP包,两次传输之间时间差不大 */
  if (priv->last_elapsed && elapsed <= priv->last_elapsed)
    return;

  priv->last_elapsed = elapsed;

  /* this is the total time we need to play */
  total = priv->npt_stop - priv->npt_start;
  GST_LOG_OBJECT (jitterbuffer, "total %" GST_TIME_FORMAT,
      GST_TIME_ARGS (total));

  /* this is how much time there is left,剩下多长时间没有播放 */
  if (total > elapsed)
    left = total - elapsed;
  else
    left = 0;

  /* if we have less time left that the size of the buffer, we will not
   * be able to keep it filled, disabled buffering then余下没有播放的时长太短,取消buffering */
  if (left < rtp_jitter_buffer_get_delay (priv->jbuf)) {
    GST_DEBUG_OBJECT (jitterbuffer, "left %" GST_TIME_FORMAT
        ", disable buffering close to EOS", GST_TIME_ARGS (left));
    rtp_jitter_buffer_disable_buffering (priv->jbuf, TRUE);
  }

  /* this is the current time as running-time */
  out_time = item->pts;
  /* estimated = item->pts * total / elapsed ,正常情况下item->pts约等于elapsed,也就是EOS的时间约为total
   * 也就是说,EOS的Timer只应该有一个,且是在total时间过后被触发
   * 但如果elapsed因故过多,则estimated则会变小,EOS提前,基本上可以理解成结束需要大约多久,正常应为duration 
  * EOS剩下时间估算时间的方式为estimated = out_time * total / elapsed
  * 其中out_time已经播放的时间(单位为纳秒),total是总时间,elapsed是播放经过的绝对系统时间。
*/
  if (elapsed > 0)
    estimated = gst_util_uint64_scale (out_time, total, elapsed);
  else {
    /* if there is almost nothing left,
     * we may never advance enough to end up in the above case */
    if (total < GST_SECOND)
      estimated = GST_SECOND;
    else
      estimated = -1;
  }
  GST_LOG_OBJECT (jitterbuffer, "elapsed %" GST_TIME_FORMAT ", estimated %"
      GST_TIME_FORMAT "eos %" GST_TIME_FORMAT, GST_TIME_ARGS (elapsed),
      GST_TIME_ARGS (estimated), GST_TIME_ARGS(priv->estimated_eos));

  if (estimated != -1 && priv->estimated_eos != estimated) {
    rtp_timer_queue_set_eos (priv->timers, estimated,
        timeout_offset (jitterbuffer));
    priv->estimated_eos = estimated;
  }
}

2.取出Timer
取出Timer并将EOS事件放到RTPJitterBuffer队列中,这个timer要么按它最可能发生的时间放在队列中,要么按最小seqnum优先的顺序被放到队列中。流程如下:

queue_do_insert                    gst-plugins-good\gst\rtpmanager\rtpjitterbuffer.c(1156)
rtp_jitter_buffer_insert        gst-plugins-good\gst\rtpmanager\rtpjitterbuffer.c(1156)
rtp_jitter_buffer_append_event    gst-plugins-good\gst\rtpmanager\rtpjitterbuffer.c(1150)
queue_event                        gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(1825)
do_eos_timeout                    gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(4034)
do_timeout                        gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(4034)
wait_next_timeout                gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(4119)

3.
取出RTPJitterBuffer队列中的事件
,EOS事件在如下线程中被取出来,发送给下游后续的元素,结束播放:

gst_pad_push_event                gstreamer\gst\gstpad.c(5679)
pop_and_push_next                gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(3623)
gst_rtp_jitter_buffer_loop        gst-plugins-good\gst\rtpmanager\gstrtpjitterbuffer.c(4223)

        取出Timer的依据如下:

    /* 取得当前时间 */
    if (priv->eos) {
      now = GST_CLOCK_TIME_NONE;
    } else if (GST_ELEMENT_CLOCK (jitterbuffer)) {
      now =
          gst_clock_get_time (GST_ELEMENT_CLOCK (jitterbuffer)) -
          GST_ELEMENT_CAST (jitterbuffer)->base_time;
    }
    /* 遍历所有Timer,取出比当前时间小的Timer,因为时间已经过了 */
    while ((timer = rtp_timer_queue_pop_until (priv->timers, now)))
      do_timeout (jitterbuffer, timer, now, &events);

如果在VC调试的时候,不能断住,日志打印可以行,Gstreamer里面,gstinfo.c中可以找出void gst_debug_print_stack_trace (void)和gchar *gst_debug_get_stack_trace (GstStackTraceFlags flags)函数,用来打印调用栈信息。

说明:

1.        gst_rtp_jitter_buffer_loop处理传入的RTP 数据包,将其缓冲直到可以按正确顺序播放出。由于网络拥塞或其他因素,RTP 数据包有时会以不规则的间隔到达。GstRtpJitterBuffer将数据包保留直到可以按正确顺序排序和发送,确保音频或视频流畅播放,没有任何跳跃或故障。
2.        pop_and_push_next(GstRtpJitterBuffer *jitterbuffer, guint seqnum)用于删除队列中指定序列号的数据包,当该函数被调用时,它会查找第二个参数指定序列号的数据包并删除它,然后将下一个数据包推到队列的前面。
3.        rtp_jitter_buffer_pop(RTPJitterBuffer *jbuf, gint *percent) 用于从缓冲区中取出第一个数据包,并计算缓冲区的占用百分比。percent变量的指针用于存储缓冲区的占用百分比。如果缓冲区中没有数据包可用,则函数返回 NULL。在这种情况下,percent 会被设置为 0。通过计算缓冲区的占用百分比,可以确定缓冲区是否已满或空,并做出相应的处理。
4.        update_estimated_eos用于更新媒体流的预估结束时间戳 (EOS)。预估的 EOS 时间戳用于确定媒体流预计何时结束,函数计算媒体流中已处理的最大时间戳,即时长。然后,基于假设不会再接收到时间戳大于最大时间戳的缓冲区,使用该最大时间戳来预估流的结束时间。
在需要知道流预计何时结束的应用中使用 update_estimated_eos 函数。通过定期更新预估的 EOS 时间戳,这些应用程序可以准确地确定何时停止播放或记录流。

5.         RTST中有一个专门的线程,用来取超时的timer,即wait_next_timeout。
wait_next_timeout (GstRtpJitterBuffer *jitterbuffer) 用于等待下一个超时事件。函数从timer的队列中取出timer,已经超时,就执行超时timer对应的类型函数处理;如果没有超时,则会调用
gst_clock_id_wait来等待特定的时钟 ID 到达指定的时间。该函数等待的时间以纳秒为单位,会阻塞当前线程,直到时钟 ID 的时间等于或超过指定的时间,函数继续循环执行超时事件,所以说有Timer,没超时也会等到超时。如果等待时间过长(例如,超过了指定的时间),函数将返回 GST_CLOCK_UNSCHEDULED。如果等待成功,则返回 GST_CLOCK_OK。

6. 发送方的时间差体现在rtp_time上,接收方的时间差体现在base_time上。

7. rtp_jitter_buffer_calculate_pts 会重置RTPJitterBuffer,包括重置base_time,base_extrtp,base_rtptime等等。原因是:
1. 收到第一个RTP数据包时
2. RTP时间戳回绕或者小于base_rtp时间戳时,
3. 是当 RTP 数据包之间的时间戳间隔为0时,就会产生一个间隔为0的 gap。如果不重置 ext_rtptime,就有可能会导致 PTS 计算错误,从而影响音视频的同步性和流畅性。
4. 当 RTP 时间戳和系统时间之间存在较大差别时重置。

WMS服务器支持

        RTSP连接命令用gst_rtspsrc_init_request来初始化,每个RTP的session都包括两个sink,一个是rtp的,另外一个是rtcp的。RTP的session数据源于理底层的UDP/TCP source,同样,每个RTP的session都包括两个TCP/UDP Source和当前的rtp/rtcp sink连接。客户端连接服务器发送RTSP描述请求(DESCRIBE request),服务器反馈SDP(Session DescriptionProtocol)信息,包括流数量、媒体类型等。客户端分析SDP描述,为RTSP SRC中每条流调用gst_rtspsrc_create_stream建立strem结构,发送RTSP连接建立请求(SETUPrequest)并告诉Server接收数据的端口,Server响应该请求(SETUP response)并建立连接之后,传送媒体流(RTP包)到客户端。
        每条流通过gst_rtspsrc_setup_streams_start来创建session,流程如下:
gst_rtspsrc_open->gst_rtspsrc_open_from_sdp->gst_rtspsrc_setup_streams_start->gst_rtsp_src_setup_stream_from_response->gst_rtspsrc_stream_configure_transport->gst_rtspsrc_stream_configure_manager->gst_element_request_pad_simple->_gst_element_request_pad->gst_rtp_bin_request_new_pad->create_recv_rtp->create_session
客户端发送终止请求(TEARDOWN request)来结束流媒体会话。
new_ssrc_pad_found回调函数用来产生GstRtpBinStream。
对于Windows Media Services (WMS) 流需要特别注意:
1 application streams只能用UDP传输
2 所有的流都在一个UDP数据连接上面传输
3 SDP中的RTX流不需要
4 第一条流建立成功之后,其余的2,3……条流建立连接会返回461协议错误。
虽然如此,但是在SETUP阶段,所有流都是需要的,如果忘记SETUP audio,那么audio将不会被传输。一个有三条流,rtx/a/v的例子如下:

OPTIONS rtsp://10.48.144.87:554/live_video RTSP/1.0
CSeq: 1
User-Agent: Lavf58.29.100

DESCRIBE rtsp://10.48.144.87:554/live_video RTSP/1.0
Accept: application/sdp
CSeq: 2
User-Agent: Lavf58.29.100

SETUP rtsp://10.48.144.87:554/live_video/rtx RTSP/1.0
Transport: RTP/AVP/UDP;unicast;client_port=21826-21827;mode=play
CSeq: 3
User-Agent: Lavf58.29.100
--
SETUP rtsp://10.48.144.87:554/live_video/video RTSP/1.0
Transport: RTP/AVP/UDP;unicast;client_port=21828;mode=play
CSeq: 4
User-Agent: Lavf58.29.100
Session: 5861654706171040746
--
SETUP rtsp://10.48.144.87:554/live_video/audio RTSP/1.0
Transport: RTP/AVP/UDP;unicast;client_port=21828;mode=play
CSeq: 5
User-Agent: Lavf58.29.100
Session: 5861654706171040746
--
PLAY rtsp://10.48.144.87:554/live_video/ RTSP/1.0
Range: npt=0.000-
CSeq: 6
User-Agent: Lavf58.29.100
Session: 5861654706171040746
--
[asf demuxer @ 0DDD3540] key:1 stream:0 seq:154 offset:0 replic_size:8 num:81 packet_property 5D
[asf demuxer @ 0DDD3540] key:0 stream:1 seq:173 offset:0 replic_size:8 num:2 packet_property 5D
[asf demuxer @ 0DDD3540] key:1 stream:-1 seq:61569 offset:-431887785 replic_size:0 num:CC packet_property 2C

Realmedia-style server也是需要特殊处理的。
        常用的视频直播协议有MP2T/RTP/UDP和MP2T/UDP,MP2T是mpeg2-ts,使用UDP的主要原因是直播视频不需要重传。将7个MP2T包打包为一个RTP,每个RTP包打包为一个UDP。或将7个MP2T包直接打包为一个UDP包。

        对于Windows Media Services,Gstreamer播放有一个坑,直播但是却携带了时长信息及播放了多久用于计算elapsed的信息,_RTPJitterBufferItem中的rtp time带上了当前服务器已经播放的时间,elapsed会很大,见如下日志,这个时候,明明才开始播放,由于这个坑,计算出来的estimated很小,播放会很快结束。只要当前已经播放的a/v小于elapsed的时间,estimated下降的很快,从而会导致提前EOS。

[16:17:32 158] [I] [rtpsession][rtpsession0] pushing received RTP packet
[16:17:32 158] [I] [rtpjitterbuffer][rtpjitterbuffer0] total 0:15:40.133000000
[16:17:32 158] [I] [rtpssrcdemux][rtpssrcdemux0] received buffer of SSRC d5c47369
[16:17:32 158] [I] [rtpjitterbuffer][rtpjitterbuffer0] elapsed 0:02:21.166000000 estimated 0:00:00.219772388 eos 99:99:99.999999999 time 0:00:00.033000000 left 0:13:18.967000000

推流工具

        推流工具可以选用live555MediaServer,EasyDarwin,EasyDarwin使用说明见下面URL:Releases · EasyDarwin/EasyDarwin · GitHub

这里有一小段批处理脚本来实现推流功能:

::rem ffmpeg -re -i F:\clip\mkv\007_X264_6audio_5sub.mkv -rtsp_transport tcp -vcodec copy -f rtsp rtsp://10.48.144.135/rtsp
set CURR_DIR=%cd%
SET PATH=%PATH%;%CURR_DIR%\EasyDarwin-windows-8.1.0-1901141151
start EasyDarwin.exe
::必须先等待EasyDarwin软件准备好后再用FFMPEG推流
sleep 20
goto:start
:start
    ffmpeg -re -stream_loop -100 -i F:\clip\mkv\007_X264_6audio_5sub.mkv -rtsp_transport udp -codec copy -f rtsp rtsp://10.48.144.135/rtsp
    if %errorlevel% EQ 0 goto :start

        顺便总结一下RTMP直播流的推流服务器建立:

1. 下载nginx ,笔者下载了 http://nginx-win.ecsds.eu/download/nginx 1.7.11.3 Gryphon.zip这个版本。
        A. 下载后解压随意放到一个文件夹下,注意,这个文件夹的名字最好修改一下,如修改成nginx_1_7_11_3_Gryphon,不要含有空格和“."这些字符,以免有的系统不识别。
        B. 在nginx_1_7_11_3_Gryphon根目录下创建tmp/hls两级目录

       nginx充当了反向代理商服务器的功能, 客户端只需要将请求发送到反向代理服务器,反向代理服务器去选择目标服务器,获取数据后再返回给客户端,客户端不需要任何配置。
2.下载https://github.com/arut/nginx-rtmp-module/, 并将nginx-rtmp-module放置到nginx的根目录下。
3.复制nginx-win-rtmp.conf成nginx.conf,并在其中加入RTMP相关内容,如下:gst_rtsp_extension_send,音视频,实时音视频,gstreamer
4.打开cmd终端,cd到nginx_1_7_11_3_Gryphon目录下,执行nginx.exe即可。
可以打开http://localhost/  查看nginx工作是否正常。
5.用FFMPEG推流,命令如下:
    ffmpeg -re -stream_loop -1 -i F:\clip\flv\Oh_MV_AVC1_AAC_1080.f4v -codec copy -f flv rtmp://10.48.144.135:1935/rtmp
6.用其他播放器打开地址 rtmp://10.48.144.135:1935/live/rtmp即可播放。

来一个脚本支持推流:

set CURR_DIR=%cd%
SET PATH=%PATH%;%CURR_DIR%\nginx_1.7.11.3_Gryphon

nginx.exe -s stop 
nginx.exe -s quit
taskkill /F /IM nginx.exe

start nginx.exe
sleep 4
:start
    ffmpeg -re -stream_loop -1 -i F:\clip\flv\Oh_MV_AVC1_AAC_1080.f4v -codec copy -f flv rtmp://10.48.144.135:1935/live/rtmp
    goto :start

错误1:nginx: [emerg] CreateDirectory() "/tmp/hls" failed (3: The system cannot find the path specified)
可以将 /tmp/hls 修改成 ./tmp/hls 即可解决问题
错误2:ffmpeg推流报I/O错误 或者 Cannot open connection tcp,记得将 nginx.conf 文件中的application 后面的名称用到直播流的第一级目录上,见上述图片描述。文章来源地址https://www.toymoban.com/news/detail-660617.html

到了这里,关于从零开始成为GStreamer专家——RTSP播放开发的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从零开始学习Linux运维,成为IT领域翘楚(十)

    防火墙管理工具 firewalld概述 Centos 系统中集成了多款防火墙管理工具,其中 firewalld服务是默认的防火墙配置管理工具,它拥有基于 CLI(命 令行界面)和基 于 GUI(图形用户界面)的两种管理方式。 firewalld 中常用的区域名称及策略规则 区域 默认策略规则 trusted 允许所有的数

    2024年02月03日
    浏览(55)
  • 从零开始学习Linux运维,成为IT领域翘楚(二)

    文件系统组织结构 登录系统后,在当前命令窗口下输入命令: 用户管理概述 Linux是一个多用户、多任务的操作系统。 用户账号和用户组 用户概念 用户组概念    用户组(group)就是具有相同特征的用户(user)的集合体;比如有时我们要让多个用户具有相同的权限,比如查

    2024年02月01日
    浏览(40)
  • 2.gstreamer USB摄像头RTSP推流

    目录 1、操作系统版本 2、使用gstreamer播放mp4文件 3、采集USB摄像头视频源,并RTSP推流 4、使用RTSP播放器播放 5、注意事项 使用的虚拟机加ubuntu 20.04 这里需要使用MobaXterm ssh登录,可以正常播放,但是在虚拟机内无法播放,暂时不知道原因 先安装编译gstreamer rtsp需要用到的工具

    2023年04月19日
    浏览(56)
  • opencv GStreamer拉rtsp流之Windows平台

    opencv要想使用GStreamer拉rtsp流,那么编译opencv必须带上GStreamer编译选项,具体参见:opencv带GStreamer之Windows编译 rtspsrc location={rtsp_url} :这是GStreamer的元素,用于指定RTSP流的位置。 {rtsp_url} 是您实际的RTSP流URL,包括用户名、密码、IP地址、端口号和流路径。 latency=0 :这是 rtsp

    2024年02月13日
    浏览(81)
  • 海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储

    文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/131679108 上一篇:没有了 下一篇:敬请期待…   Hik防爆摄像头录像,因为防爆摄像头会有对应的APP软件,与普通的网络摄像头和球机不一样,默认认为它不可以通过web网页配置,所以弄

    2024年02月16日
    浏览(41)
  • 从零开始学习go开发

    1.Go 语言的特点和优势 Go 语言是一门相对年轻的编程语言,它具有以下特点和优势: 并发编程能力:Go 语言天生支持并发编程,使用 goroutine 和 channel 可以轻松实现并发操作,让编程更加高效。 高效的内存管理:Go 语言使用垃圾回收机制进行内存管理,省去了手动管理内存的

    2024年02月15日
    浏览(70)
  • 如何才能成为数字IC后端ECO专家?

    文章右侧广告为官方硬广告,与吾爱IC社区无关,用户勿点。点击进去后出现任何损失与社区无关。 临近 618,这几天很多粉丝私信表达希望小编知识星球搞个优惠活动。但是之前也有收到不少朋友的私信,有的是希望星球要提高门槛,因为觉得人数多了,大家都学会了,以后

    2023年04月09日
    浏览(44)
  • 如何从零开始开发一个小程序

    申请账号 小程序注册页 开发设置 登录 小程序后台 ,我们可以点击左侧菜单 “开发”-“开发管理”,点击后正文上方点击 “开发设置” ,就看到小程序的 AppID(小程序ID) 了 。 小程序的 AppID 相当于小程序平台的一个身份证,后续你会在很多地方要用到 AppID (注意这里要区别

    2024年02月10日
    浏览(62)
  • 从零开始的PHP开发逆天路——语法

    PHP 脚本在服务器上执行,然后将纯 HTML 结果发送回浏览器。 PHP 脚本可以放在文档中的任何位置。 PHP 脚本以 ?php 开始,以 ? 结束: ?php // PHP 代码 ? PHP 文件的默认文件扩展名是 .php。 PHP 文件通常包含 HTML 标签和一些 PHP 脚本代码。 向浏览器输出文本 \\\"Hello World!\\\": !DOCTYPE htm

    2024年04月17日
    浏览(40)
  • 从零开始的种田生活-Unity游戏开发

    大家好,这里是暴躁老哥酒九。最近了我们的童年记忆《摩尔庄园》在手机上面复活了,不知道大家有没有沉迷于种菜无法自拔呢(反正我是累了)。 种田才是这个游戏本质吧~ 在《摩尔庄园》中了为我们玩家提供了很多的玩法比如:钓鱼,烹饪,开餐厅,庄园和玩家自身装

    2024年02月02日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包