gstreamer-hlsdemux插件的初始流程

hls_type_find初始化

在gst-plugins-base/gst/typefind/gsttypefindfunctions.c代码中会初始化typefind,不同的扩展名对应不同的函数,hls的扩展名是m3u8,type_find函数是hls_type_find:

  TYPE_FIND_REGISTER (plugin, "application/x-hls", GST_RANK_MARGINAL,
                      hls_type_find, "m3u8", HLS_CAPS, NULL, NULL);

hls_type_find的调用栈

1  hls_type_find                                gsttypefindfunctions.c 526  0x7fffde8da834
2  gst_type_find_factory_call_function          gsttypefindfactory.c   216  0x7ffff7b4bfd3
3  gst_type_find_helper_for_data_with_extension gsttypefindhelper.c    666  0x7ffff5122a8f
4  gst_type_find_element_chain_do_typefinding   gsttypefindelement.c   956  0x7fffef66489d
5  gst_type_find_element_sink_event             gsttypefindelement.c   700  0x7fffef663e40

gst_type_find_factory_call_function

gstreamer/gst/gsttypefindfactory.c

其中gst_type_find_element_chain_do_typefinding调用gst_type_find_get_extension从uri中获得扩展名m3u8,然后gst_type_find_helper_for_data_with_extension根据m3u8的扩展名获取caps,prioritize_extension会从factory的type_list里面逐个去找,判断每一个factory的extension是不是m3u8,如果是就停止,这时候从type_list里面删除当前node,然后插入factory到type_list的最前面。

然后从前面的节点中得到helper.factory,然后gst_type_find_factory_call_function调用factory:

    helper.factory = GST_TYPE_FIND_FACTORY (l->data);
    gst_type_find_factory_call_function (helper.factory, &find);

然后gst_type_find_factory_call_function调用,就是前面栈顶的hls_type_find

  // 根据factory 创建typefind factory
  GstTypeFindFactory *new_factory;
  new_factory = GST_TYPE_FIND_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE(factory)));
  if (new_factory) {
    if (new_factory->function)
      // hls_type_find就是在开始TYPE_FIND_REGISTER初始化的function
      new_factory->function (find, new_factory->user_data);
    gst_object_unref (new_factory);
  }

gst_type_find_factory_call_function函数

static GList *
prioritize_extension (GstObject * obj, GList * type_list,
    const gchar * extension)
{
  gint pos = 0;
  GList *next, *l;

  if (!extension)
    return type_list;

  /* move the typefinders for the extension first in the list. The idea is that
   * when one of them returns MAX we don't need to search further as there is a
   * very high chance we got the right type. */

  GST_LOG_OBJECT (obj, "sorting typefind for extension %s to head", extension);

  for (l = type_list; l; l = next) {
    const gchar *const *ext;
    GstTypeFindFactory *factory;

    next = l->next;

    factory = GST_TYPE_FIND_FACTORY (l->data);

    // 依次对比每一个factory的extension
    ext = gst_type_find_factory_get_extensions (factory);
    if (ext == NULL)
      continue;

    GST_LOG_OBJECT (obj, "testing factory %s for extension %s",
        GST_OBJECT_NAME (factory), extension);

    while (*ext != NULL) {
      if (strcmp (*ext, extension) == 0) {
        /* found extension, move in front */
        GST_LOG_OBJECT (obj, "moving typefind for extension %s to head",
            extension);
        /* remove entry from list */
        // 删除node l
        type_list = g_list_delete_link (type_list, l);
        /* insert at the position */
        // 插入到type_list最前面
        type_list = g_list_insert (type_list, factory, pos);
        /* next element will be inserted after this one */
        pos++;
        break;
      }
      ++ext;
    }
  }
  return type_list;
}

在hls_type_find函数中还有对数据的判断:

    /* Search for # comment lines */
    if (c.data[0] == '#' && (memcmp (c.data, "#EXT-X-TARGETDURATION", 21) == 0
            || memcmp (c.data, "#EXT-X-STREAM-INF", 17) == 0
            || memcmp (c.data, "#EXT-X-MEDIA", 12) == 0)) {
      gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, HLS_CAPS);
      return;
    }

然后在gst_type_find_element_emit_have_type函数中通过g_signal_emit emit这个caps,type_found中根据factory创建新的element,factory的caps为application/x-hls,自然就找到了hlsdemux。

    if (analyze_new_pad (decode_bin, typefind, pad, caps,
            decode_bin->decode_chain, NULL))
      expose_pad (decode_bin, typefind, decode_bin->decode_chain->current_pad,
          pad, caps, decode_bin->decode_chain);

hlsdemux调用栈

1  gst_hls_demux_class_init                   gsthlsdemux.c        148  0x7fffde7bc334
2  gst_hls_demux_class_intern_init            gsthlsdemux.c        122  0x7fffde7bc1b2
3  g_type_class_ref                                                     0x7ffff7522439
4  g_object_new_with_properties                                         0x7ffff7507ea8
5  gst_element_factory_create_with_properties gstelementfactory.c  438  0x7ffff7adb2d3
6  gst_element_factory_create                 gstelementfactory.c  598  0x7ffff7adb865
7  connect_pad                                gstdecodebin2.c      2296 0x7ffff5b3a003
8  analyze_new_pad                            gstdecodebin2.c      1836 0x7ffff5b384d8
9  type_found                                 gstdecodebin2.c      2911 0x7ffff5b3c940

hlsdemux的m3u8处理过程

1   gst_hls_master_playlist_new_from_data m3u8.c               1597 0x7fffde7ccea9
2   gst_hls_demux_process_manifest        gsthlsdemux.c        779  0x7fffde7be2f1

gst_hls_master_playlist_new_from_data中根据m3u8的具体每一个标签进行解析,在前面的index.m3u8中,最先解析的自然是#EXT-X-MEDIA标签。

#Audio 注释
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio/dtse",LANGUAGE="en",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI
="audio-en-dtse.m3u8"

然后解析#EXT-X-STREAM-INF:或者#EXT-X-I-FRAME-STREAM-INF:,解析标签中带的各个属性。

#EXT-X-STREAM-INF:AUDIO="audio/dtse",AVERAGE-BANDWIDTH=699465,BANDWIDTH=744010,CODECS="avc1.42C01E,dtse",
RESOLUTION=640x360
video-avc1-1.m3u8

如果不是#开始的就会解析为数据,比如本例中的video-avc1-1.m3u8,base_uri是http://cert.streamdts.com/dtse/hls/fmp4/chid/index.m3u8

在第一个标签下面解析出来的uri则是http://cert.streamdts.com/dtse/hls/fmp4/chid/video-avc1-1.m3u8

#EXT-X-STREAM-INF说明是多码率码流,选择第一个作为playlist->default_variant,依次解析#EXT-X-STREAM-INF,并将其添加到多码率列表里。

        gst_m3u8_set_uri (pending_stream->m3u8, uri, NULL, name);
        playlist->variants = g_list_append (playlist->variants, pending_stream);
        /* use first stream in the playlist as default */
        if (playlist->default_variant == NULL) {
          playlist->default_variant = gst_hls_variant_stream_ref (pending_stream);
        }

gst_hls_demux_update_playlist

然后gst_hls_demux_update_playlist中gst_m3u8_get_uri获取当前demux->current_variant->m3u8(在本例中的值是http://cert.streamdts.com/dtse/hls/fmp4/chid/video-avc1-1.m3u8),并且gst_uri_downloader_fetch_uri去获取uri的数据。

  uri = gst_m3u8_get_uri (demux->current_variant->m3u8);
  main_uri = gst_adaptive_demux_get_manifest_ref_uri (adaptive_demux);
  download = gst_uri_downloader_fetch_uri (adaptive_demux->downloader,
  							uri, main_uri, TRUE, TRUE, TRUE, err);

然后再处理video-avc1-1.m3u8的内容,gst_m3u8_update又会解析一遍video-avc1-1.m3u8,如果是#EXTINF,这个标签对应的属性都会被解析,如果不是以#开头,那么就能解析到media/svk-v001-video-avc1-1.mp4,通过uri_join函数组合出了流的uri http://cert.streamdts.com/dtse/hls/fmp4/chid/media/svk-v001-video-avc1-1.mp4

  buf = gst_fragment_get_buffer (download);
  playlist = gst_hls_src_buf_to_utf8_playlist (buf);
  gst_buffer_unref (buf);
  g_object_unref (download);

  if (playlist == NULL) {
    GST_WARNING_OBJECT (demux, "Couldn't validate playlist encoding");
    g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
        "Couldn't validate playlist encoding");
    return FALSE;
  }

  // 解析video-avc1-1.m3u8
  if (!gst_m3u8_update (m3u8, playlist)) {
    GST_WARNING_OBJECT (demux, "Couldn't update playlist");
    g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED,
        "Couldn't update playlist");
    return FALSE;
  }

gst_m3u8_update

调用栈如下:

1   gst_m3u8_update                  m3u8.c               484  0x7fffde7c8caf
2   gst_hls_demux_update_playlist    gsthlsdemux.c        1649 0x7fffde7c1354
3   gst_hls_demux_process_manifest   gsthlsdemux.c        810  0x7fffde7be553
4   gst_adaptive_demux_sink_event    gstadaptivedemux.c   686  0x7fffde59ebdc

gst_m3u8_update中解析video-avc1-1.m3u8之后,通过uri_join函数组合的uri(http://cert.streamdts.com/dtse/hls/fmp4/chid/media/svk-v001-video-avc1-1.mp4)会创建为GstM3U8MediaFile的文件,然后添加到m3u8的list里面,list的添加是后面解析的存在list的前面,到最后处理完后会revert。

    if (data[0] != '#' && data[0] != '\0') {
      if (duration <= 0) {
        GST_LOG ("%s: got line without EXTINF, dropping", data);
        goto next_line;
      }

      data = uri_join (self->base_uri ? self->base_uri : self->uri, data);

	 // 加到list里面
	 self->files = g_list_prepend (self->files, file);

下面是通过解析self->files内存得到的截图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DDaelLvV-1671949631341)(.images/image-20220419172612973.png)]

从下往上看,刚好和video-avc1-1.m3u8中的duration可以对应上,其中file->sequence是在创建file的按照m3u8中的文件次序加上去的。这个实际上并不是常见的按时常分片的hls源,是#EXT-X-BYTERANGE标签指定的分区。

#EXT-X-BYTERANGE:89897@831
media/svk-v001-video-avc1-1.mp4
#EXTINF:2.002000,
#EXT-X-BYTERANGE:74432@90728
media/svk-v001-video-avc1-1.mp4
#EXTINF:2.002000,
#EXT-X-BYTERANGE:72291@165160
media/svk-v001-video-avc1-1.mp4
#EXTINF:2.002000,
#EXT-X-BYTERANGE:72208@237451
media/svk-v001-video-avc1-1.mp4

然后解析media,还是在gst_hls_demux_update_playlist函数中,这部分代码解析的是#EXT-X-MEDIA标签指定的,调用函数gst_hls_demux_update_rendition_manifest,完成对http://cert.streamdts.com/dtse/hls/fmp4/chid/audio-en-dtse.m3u8的处理,同样会调用gst_m3u8_update对audio-en-dtse.m3u8文件进行解析,生成对应的list,存储在(GstHLSMedia*)media->playlist->files中。

  for (i = 0; i < GST_HLS_N_MEDIA_TYPES; ++i) {
    GList *mlist = demux->current_variant->media[i];

    while (mlist != NULL) {
      GstHLSMedia *media = mlist->data;

      if (media->uri == NULL) {
        /* No uri means this is a placeholder for a stream
         * contained in another mux */
        mlist = mlist->next;
        continue;
      }
      GST_LOG_OBJECT (demux,
          "Updating playlist for media of type %d - %s, uri: %s", i,
          media->name, media->uri);

      if (!gst_hls_demux_update_rendition_manifest (demux, media, err))
        return FALSE;

      mlist = mlist->next;
    }
  }

最后进入gst_hls_demux_setup_streams,创建stream。

gst_hls_demux_setup_streams

在这个函数中会创建stream,create_stream_for_playlist中会创建hlsdemux_stream,因为这个m3u8文件中有#EXT-X-MEDIA标签,所以main playlist有一个,media的playlist也有一个。

  /* 1 output for the main playlist */
  create_stream_for_playlist (demux, playlist->m3u8, TRUE, TRUE);

  for (i = 0; i < GST_HLS_N_MEDIA_TYPES; ++i) {
    GList *mlist = playlist->media[i];
    while (mlist != NULL) {
      GstHLSMedia *media = mlist->data;

      if (media->uri == NULL /* || media->mtype != GST_HLS_MEDIA_TYPE_AUDIO */ ) {
        /* No uri means this is a placeholder for a stream
         * contained in another mux */
        GST_LOG_OBJECT (demux, "Skipping stream %s type %s with no URI",
            media->name, gst_hls_media_type_get_name (media->mtype));
        mlist = mlist->next;
        continue;
      }
      GST_LOG_OBJECT (demux, "media of type %s - %s, uri: %s",
          gst_hls_media_type_get_name (i), media->name, media->uri);
      create_stream_for_playlist (demux, media->playlist, FALSE,
          (media->mtype == GST_HLS_MEDIA_TYPE_VIDEO
              || media->mtype == GST_HLS_MEDIA_TYPE_AUDIO));

      mlist = mlist->next;
    }
  }

调用栈如下:

1   create_stream_for_playlist       gsthlsdemux.c        516  0x7fffde7bd267
2   gst_hls_demux_setup_streams      gsthlsdemux.c        680  0x7fffde7bdc34
3   gst_hls_demux_process_manifest   gsthlsdemux.c        819  0x7fffde7be653

download_task

完成了前面这些工作之后,就会start download_task:

gst_task_start (stream->download_task);

download task是在gst_adaptive_demux_stream_new中初始化的,gst_adaptive_demux_stream_new的栈:

1  gst_adaptive_demux_stream_new    gstadaptivedemux.c   1313 0x7fffde5a131e
2  create_stream_for_playlist       gsthlsdemux.c        508  0x7fffde7bd233
3  gst_hls_demux_setup_streams      gsthlsdemux.c        680  0x7fffde7bdc34
4  gst_hls_demux_process_manifest   gsthlsdemux.c        819  0x7fffde7be653

stream->download_task赋值:

  /* Downloading task */
  g_rec_mutex_init (&stream->download_lock);
  stream->download_task =
      gst_task_new ((GstTaskFunction) gst_adaptive_demux_stream_download_loop, stream, NULL);
  gst_task_set_lock (stream->download_task, &stream->download_lock);

start task是在process_manifest之后,前面create_stream_for_playlist已经创建了stream,在这段代码里面先prepare stream,然后开始download_task:

        if (demux->next_streams) {
          gst_adaptive_demux_prepare_streams (demux, gst_adaptive_demux_is_live (demux));
          gst_adaptive_demux_start_tasks (demux, TRUE);
          gst_adaptive_demux_start_manifest_update_task (demux);
        }

download_task停止:

在gst_soup_http_src_read_buffer部分,如果读不到buffer就会返回GST_FLOW_EOS,gst_base_src_loop中会根据GST_FLOW_EOS创建EOS event,然后push到pad上,gst_uri_downloader_sink_event中再释放downloader->priv->cond条件变量,这个条件变量会结束downloader uri的线程的等待,在gst_uri_downloader_fetch_uri_with_range中可以看到。

static gboolean
gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  gboolean ret = FALSE;
  GstUriDownloader *downloader;

  downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));

  switch (event->type) {
    case GST_EVENT_EOS:{
      GST_OBJECT_LOCK (downloader);
      GST_DEBUG_OBJECT (downloader, "Got EOS on the fetcher pad");
      if (downloader->priv->download != NULL) {
        /* signal we have fetched the URI */
        downloader->priv->download->completed = TRUE;
        downloader->priv->download->download_stop_time =
            gst_util_get_timestamp ();
        GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
        g_cond_signal (&downloader->priv->cond);
      }
      GST_OBJECT_UNLOCK (downloader);
      gst_event_unref (event);
      break;
    }

push event的栈:

gst_uri_downloader_sink_event(GstPad * pad, GstObject * parent, GstEvent * event)
gst_pad_send_event_unchecked(GstPad * pad, GstEvent * event, GstPadProbeType type)
gst_pad_push_event_unchecked(GstPad * pad, GstEvent * event, GstPadProbeType type)
push_sticky(GstPad * pad, PadEvent * ev, gpointer user_data)
events_foreach(GstPad * pad, PadEventFunction func, gpointer user_data)
check_sticky(GstPad * pad, GstEvent * event)
gst_pad_push_event(GstPad * pad, GstEvent * event)

souphttpsrc

download_task开始之后,根着这个栈,就可以看到在gst_adaptive_demux_stream_update_source中gst_element_make_from_uri根据参数uri(http://cert.streamdts.com/dtse/hls/fmp4/chid/media/svk-v001-video-avc1-1.mp4,因为有media标签,所以还有一个download_task会下载http://cert.streamdts.com/dtse/hls/fmp4/chid/media/svk-v001-audio-en-dtse.mp4)创建souphttpsrc,然后将souphttpsrc的srcpad和queue的sinkpad链接到一起。

1  gst_adaptive_demux_stream_update_source            gstadaptivedemux.c 3101 0x7fffde5aa537
2  gst_adaptive_demux_stream_download_uri             gstadaptivedemux.c 3202 0x7fffde5aab71
3  gst_adaptive_demux_stream_download_header_fragment gstadaptivedemux.c 3363 0x7fffde5ab9d4
4  gst_adaptive_demux_stream_download_fragment        gstadaptivedemux.c 3418 0x7fffde5abc72
5  gst_adaptive_demux_stream_download_loop            gstadaptivedemux.c 3825 0x7fffde5adf61
    uri_handler_src = gst_element_get_static_pad (uri_handler, "src");
    queue_sink = gst_element_get_static_pad (queue, "sink");

    pad_link_ret = gst_pad_link_full (uri_handler_src, queue_sink, GST_PAD_LINK_CHECK_NOTHING);

同时gst_adaptive_demux_stream_update_source中创建了internal-pad,具体的下载就是有internal-pad完成,_src_chain

    internal_name = g_strdup_printf ("internal-%s", GST_PAD_NAME (stream->pad));
    stream->internal_pad = gst_pad_new (internal_name, GST_PAD_SINK);
    g_free (internal_name);
    gst_object_set_parent (GST_OBJECT_CAST (stream->internal_pad),
        GST_OBJECT_CAST (demux));
    GST_OBJECT_FLAG_SET (stream->internal_pad, GST_PAD_FLAG_NEED_PARENT);
    gst_pad_set_element_private (stream->internal_pad, stream);
    gst_pad_set_active (stream->internal_pad, TRUE);
    gst_pad_set_chain_function (stream->internal_pad, _src_chain);
    gst_pad_set_event_function (stream->internal_pad, _src_event);
    gst_pad_set_query_function (stream->internal_pad, _src_query);

gst_adaptive_demux_stream_download_uri

souphttpsrc读取buffer

1  gst_soup_http_src_read_buffer gstsouphttpsrc.c 1818 0x7ffff476bff4
2  gst_soup_http_src_create      gstsouphttpsrc.c 1927 0x7ffff476c57d
3  gst_push_src_create           gstpushsrc.c     131  0x7ffff51204a5
4  gst_base_src_get_range        gstbasesrc.c     2587 0x7ffff5100ca2
5  gst_base_src_loop             gstbasesrc.c     2911 0x7ffff51020ab

gst_adaptive_demux_stream_download_uri中调用gst_element_sync_state_with_parent的栈很长,但是可以看到在这个时候,souphttpsrc的gst_soup_http_src_start函数被调用,在gst_soup_http_src_start中调用gst_soup_http_src_session_open打开http sessioon:

1  gst_soup_http_src_session_open              gstsouphttpsrc.c   929  0x7ffff47676a1
2  gst_soup_http_src_start                     gstsouphttpsrc.c   1963 0x7ffff476c732
3  gst_base_src_start                          gstbasesrc.c       3520 0x7ffff5103fcc
4  gst_base_src_activate_push                  gstbasesrc.c       3894 0x7ffff51051a7
5  gst_base_src_activate_mode                  gstbasesrc.c       3971 0x7ffff51055cc
6  activate_mode_internal                      gstpad.c           1216 0x7ffff7afa771
7  gst_pad_activate_default                    gstpad.c           940  0x7ffff7af99fc
8  gst_pad_set_active                          gstpad.c           1099 0x7ffff7afa1a4
9  activate_pads                               gstelement.c       3165 0x7ffff7ad81a6
10 gst_iterator_fold                           gstiterator.c      617  0x7ffff7aedc5b
11 iterator_activate_fold_with_resync          gstelement.c       3189 0x7ffff7ad8249
12 gst_element_pads_activate                   gstelement.c       3225 0x7ffff7ad834b
13 gst_element_change_state_func               gstelement.c       3285 0x7ffff7ad8659
14 gst_base_src_change_state                   gstbasesrc.c       4008 0x7ffff5105735
15 gst_soup_http_src_change_state              gstsouphttpsrc.c   1998 0x7ffff476c876
16 gst_element_change_state                    gstelement.c       3077 0x7ffff7ad7cca
17 gst_element_set_state_func                  gstelement.c       3031 0x7ffff7ad7a5a
18 gst_element_set_state                       gstelement.c       2932 0x7ffff7ad7659
19 gst_bin_element_set_state                   gstbin.c           2608 0x7ffff7aa5b89
20 gst_bin_change_state_func                   gstbin.c           2950 0x7ffff7aa7226
21 gst_element_change_state                    gstelement.c       3077 0x7ffff7ad7cca
22 gst_element_set_state_func                  gstelement.c       3031 0x7ffff7ad7a5a
23 gst_element_set_state                       gstelement.c       2932 0x7ffff7ad7659
24 gst_element_sync_state_with_parent          gstelement.c       2457 0x7ffff7ad654d
25 gst_adaptive_demux_stream_download_uri      gstadaptivedemux.c 3252 0x7fffde4ad05d
26 gst_adaptive_demux_stream_download_fragment gstadaptivedemux.c 3506 0x7fffde4ae069
27 gst_adaptive_demux_stream_download_loop     gstadaptivedemux.c 3825 0x7fffde4aff61
28 gst_task_func                               gsttask.c          384  0x7ffff7b45950

gst_base_src_start:

  bclass = GST_BASE_SRC_GET_CLASS (basesrc);
  if (bclass->start)
    // 调用gst_soup_http_src_start
    result = bclass->start (basesrc);
  else
    result = TRUE;

  if (!result)
    goto could_not_start;

  if (!gst_base_src_is_async (basesrc)) {
    gst_base_src_start_complete (basesrc, GST_FLOW_OK);
    /* not really waiting here, we call this to get the result
     * from the start_complete call */
    result = gst_base_src_start_wait (basesrc) == GST_FLOW_OK;
  }

gst_base_src_start里面gst_base_src_start_complete会调用到gst_base_src_perform_seek,然后启动loop task,执行gst_base_src_loop

1  gst_base_src_perform_seek                          gstbasesrc.c       1831 0x7ffff50fde8b
2  gst_base_src_start_complete                        gstbasesrc.c       3639 0x7ffff5104690
3  gst_base_src_start                                 gstbasesrc.c       3528 0x7ffff5104003

gst_base_src_loop会循环读取buffer,直到EOS:

1  gst_soup_http_src_read_buffer gstsouphttpsrc.c 1816 0x7ffff476bfb5
2  gst_soup_http_src_create      gstsouphttpsrc.c 1927 0x7ffff476c57d
3  gst_push_src_create           gstpushsrc.c     131  0x7ffff51204a5
4  gst_base_src_get_range        gstbasesrc.c     2587 0x7ffff5100ca2
5  gst_base_src_loop             gstbasesrc.c     2911 0x7ffff51020ab
6  gst_task_func                 gsttask.c        384  0x7ffff7b45950

_src_event处理EOS

gst_soup_http_src_read_buffer读取buffer到最后一个buffer后,返回EOS,然后是在gst_base_src_loop中处理EOS返回,通过gst_pad_push_event把EOS发出去,下面是这部分的log输出:

gst_soup_http_src_update_position:<source> We're EOS now
gst_soup_http_src_create:<source> Returning -3 eos
gst_base_src_get_range:<source> create returned -3 (eos)
gst_base_src_loop:<source> pausing after gst_base_src_get_range() = eos
gst_base_src_loop:<source> pausing task, reason eos

然后_src_event收到EOS,然后判断有没有下一个fragment,返回到gst_adaptive_demux_eos_handling里面后,再调用函数gst_adaptive_demux_stream_fragment_download_finish释放条件变量fragment_download_cond。

  g_mutex_lock (&stream->fragment_download_lock);
  stream->download_finished = TRUE;
  g_cond_signal (&stream->fragment_download_cond);
  g_mutex_unlock (&stream->fragment_download_lock);
1   gst_hls_demux_stream_has_next_fragment              gsthlsdemux.c      1216 0x7fffde6c1ec9
2   gst_adaptive_demux_stream_has_next_fragment         gstadaptivedemux.c 4198 0x7fffde4b1c74
3   gst_adaptive_demux_stream_advance_fragment_unlocked gstadaptivedemux.c 4284 0x7fffde4b2246
4   gst_adaptive_demux_stream_advance_fragment          gstadaptivedemux.c 4215 0x7fffde4b1cb5
5   gst_hls_demux_finish_fragment                       gsthlsdemux.c      1120 0x7fffde6c1a90
6   gst_adaptive_demux_eos_handling                     gstadaptivedemux.c 2792 0x7fffde4aadfd
7   _src_event                                          gstadaptivedemux.c 2810 0x7fffde4aaf7f

然后又会有新的download_task。

问题:qtdemux中收到EOS的原因

因为gsteamer-低版本m3u8部分解析不支持#EXT-X-MAP字段,直接跳过了moov部分,文件头缺失,导致没有识别到可用的stream,所以最后报错是没有可用的stream。解决办法就是从高版本的gstreamer代码中找到支持#EXT-X-MAP的patch,打上patch就可以了。


参考

m3u file format

音视频编解码–M3U8文件格式

流媒体之HLS综述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值