由前第5章节分析第【1.2.1.1】小节(EsCreateDecoder实现分析:【vlc/src/input/es_out.c】中)分析可知sout媒体流输出端对象初始化大致流程概述:

// sout对象为【sout_instance_t】结构体信息(可能为空当设置了renderer时),
// 该信息定义为流输出实例即Stream Output,初始化流程:InitSout方法【vlc/src/input/input.c】-->
// input_resource_RequestSout方法【vlc/src/input/resource.c】-->sout_NewInstance方法【vlc/src/stream_out.c】,
// 并且其创建了内部字段流输出模块modules链【sout_stream_t】结构体信息。

并且若sout不为空时在【vlc/src/input/decoder.c】的【LoadDecoder】方法中会加载【module_need( p_dec, “packetizer”, “$packetizer”, false );】模块。
备注:【“packetizer”】组件名的分组分包器模块组件加载流程,见第十五章节【Part 2】部分分析

1、第三章分析[2.3.1.12.3.1.1]小节分析中播放器初始化阶段【vlc/src/input/input.c】的【Init】方法中默认开启sout流媒体输出模块的加载:

// [vlc/src/input/input.c]
static int Init( input_thread_t * p_input )
{// ...省略部分代码
#ifdef ENABLE_SOUT// 若启动了渲染器流输出链模块进行音视频输出(RTP、UDP、标准输出等),则初始化sout模块【stream_out】,// 如果已存在有效的sout则重复使用,否则创建一个新的链流输出模块。// 即根据多种输出链方式进行初始化对应的多个输出流模块即输出流模块链// 注:该实现内部有对输出链流方式如URI的解析处理等,此处不展开分析,内部的【"sout"】变量的值如输出流URIif( InitSout( p_input ) )goto error;
#endif// ...省略部分代码
}// [vlc/src/input/input.c]
static int InitSout( input_thread_t * p_input )
{input_thread_private_t *priv = input_priv(p_input);if( priv->b_preparsing )return VLC_SUCCESS;// 获取一个非空并可用的sout流媒体输出对象描述,创建后将其关联p_input输入端信息对象// 该值是播放器默认写入或用户选择的/* Find a usable sout and attach it to p_input */char *psz = var_GetNonEmptyString( p_input, "sout" );if( priv->p_renderer ){// 同时需要渲染时/* Keep sout if it comes from a renderer and if the user didn't touch* the sout config */bool keep_sout = psz == NULL;free(psz);const char *psz_renderer_sout = vlc_renderer_item_sout( priv->p_renderer );// 将psz_renderer_sout格式化后数据写入psz中if( asprintf( &psz, "#%s", psz_renderer_sout ) < 0 )return VLC_ENOMEM;if( keep_sout )var_SetBool( p_input, "sout-keep", true );}if( psz && strncasecmp( priv->p_item->psz_uri, "vlc:", 4 ) ){// 有sout对象描述且URI字符串以vlc:开头,则进入// 见下面的分析priv->p_sout  = input_resource_RequestSout( priv->p_resource, NULL, psz );if( priv->p_sout == NULL ){input_ChangeState( p_input, ERROR_S );msg_Err( p_input, "cannot start stream output instance, " \"aborting" );free( psz );return VLC_EGENERIC;}if( libvlc_stats( p_input ) ){// 初始化流媒体输出时相关统计值:发送包、字节数、码率,可用于分析问题INIT_COUNTER( sout_sent_packets, COUNTER );INIT_COUNTER( sout_sent_bytes, COUNTER );INIT_COUNTER( sout_send_bitrate, DERIVATIVE );}}else{// 根据该方法的分析,当后面两个参数为空时则表示destroy释放【缓存的】soutinput_resource_RequestSout( priv->p_resource, NULL, NULL );}free( psz );return VLC_SUCCESS;
}// [vlc/src/input/resource.c]
sout_instance_t *input_resource_RequestSout( input_resource_t *p_resource, sout_instance_t *p_sout, const char *psz_sout )
{vlc_mutex_lock( &p_resource->lock );sout_instance_t *p_ret = RequestSout( p_resource, p_sout, psz_sout );vlc_mutex_unlock( &p_resource->lock );return p_ret;
}// [vlc/src/input/resource.c]
static sout_instance_t *RequestSout( input_resource_t *p_resource,sout_instance_t *p_sout, const char *psz_sout )
{#ifdef ENABLE_SOUTif( !p_sout && !psz_sout ){// 若都为空则处理为释放当前sout对象if( p_resource->p_sout ){msg_Dbg( p_resource->p_sout, "destroying useless sout" );DestroySout( p_resource );}return NULL;}assert( !p_sout || ( !p_resource->p_sout && !psz_sout ) );// 检查已缓存的sout对象描述符类型是否为请求目标,若不是则释放/* Check the validity of the sout */if( p_resource->p_sout &&strcmp( p_resource->p_sout->psz_sout, psz_sout ) ){msg_Dbg( p_resource->p_parent, "destroying unusable sout" );DestroySout( p_resource );}if( psz_sout ){if( p_resource->p_sout ){// 此处表示,请求目标URL和当前缓存sout对象URL是相同的,因此重用即可/* Reuse it */msg_Dbg( p_resource->p_parent, "reusing sout" );msg_Dbg( p_resource->p_parent, "you probably want to use gather stream_out" );}else{// 不相同则新创建一个sout对象,并缓存起来// 见下面的分析/* Create a new one */p_resource->p_sout = sout_NewInstance( p_resource->p_parent, psz_sout );}p_sout = p_resource->p_sout;p_resource->p_sout = NULL;return p_sout;}else{// p_sout不为空而p_resource->p_sout为空时p_resource->p_sout = p_sout;return NULL;}
#elseVLC_UNUSED (p_resource); VLC_UNUSED (p_sout); VLC_UNUSED (psz_sout);return NULL;
#endif
}// 【vlc/src/stream_output/stream_output.c】
/****************************************************************************** sout_NewInstance: creates a new stream output instance*****************************************************************************/
sout_instance_t *sout_NewInstance( vlc_object_t *p_parent, const char *psz_dest )
{sout_instance_t *p_sout;char *psz_chain;assert( psz_dest != NULL );if( psz_dest[0] == '#' ){// 去掉第一个“#”字符psz_chain = strdup( &psz_dest[1] );}else{// 将输入流地址转换为流输出链接地址// 【"sout-display"是一个配置值,若为true则表示需要开启duplicate模式(复制模式)// 即本地播放和流媒体输出同时进行】// 见下面的分析psz_chain = sout_stream_url_to_chain(var_InheritBool(p_parent, "sout-display"), psz_dest );}if(!psz_chain)return NULL;// 创建"stream output"流输出对象/* *** Allocate descriptor *** */p_sout = vlc_custom_create( p_parent, sizeof( *p_sout ), "stream output" );if( p_sout == NULL ){free( psz_chain );return NULL;}// 打印流媒体输出链msg_Dbg( p_sout, "using sout chain=`%s'", psz_chain );/* *** init descriptor *** */p_sout->psz_sout    = strdup( psz_dest );p_sout->i_out_pace_nocontrol = 0;vlc_mutex_init( &p_sout->lock );p_sout->p_stream = NULL;var_Create( p_sout, "sout-mux-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );// 创建流输出【对象】链// 见下面的分析p_sout->p_stream = sout_StreamChainNew( p_sout, psz_chain, NULL, NULL );if( p_sout->p_stream ){free( psz_chain );return p_sout;}// sout创建错误msg_Err( p_sout, "stream chain failed for `%s'", psz_chain );free( psz_chain );FREENULL( p_sout->psz_sout );vlc_mutex_destroy( &p_sout->lock );vlc_object_release( p_sout );return NULL;
}// 【vlc/src/stream_output/stream_output.c】
static char *sout_stream_url_to_chain( bool b_sout_display,const char *psz_url )
{mrl_t       mrl;char        *psz_chain;// 解析access、name、way属性参数mrl_Parse( &mrl, psz_url );// 检查url是否走的是#rtp -否则我们将使用#standard/* Check if the URLs goes to #rtp - otherwise we'll use #standard */// rtp应用层协议可能使用的传输层协议集合,rtp和rtsp都是应用层协议static const char rtplist[] = "dccp\0sctp\0tcp\0udplite\0";for (const char *a = rtplist; *a; a += strlen (a) + 1)if (strcmp (a, mrl.psz_access) == 0)// 相等,走的为rtp输出流方式,则流转到rtp处理goto rtp;if (strcmp (mrl.psz_access, "rtp") == 0){// rtp传输流程// 端口号char *port;// vlc历史原因,此处rtp访问方式表示使用UDP上传输的RTP,则转换为UDP/* For historical reasons, rtp:// means RTP over UDP */strcpy (mrl.psz_access, "udp");
rtp:  // 若直接goto到此处,则代表去掉上面的udp转换处理,不会采用UDP传输数据if (mrl.psz_name[0] == '['){// 查找字符串"]:"的首次出现【其实是返回后面所有的字符串】port = strstr (mrl.psz_name, "]:");if (port != NULL)// 若不为空则代表有端口值port++;}else// 查找字符“:”port = strchr (mrl.psz_name, ':');if (port != NULL)// 去掉“:”字符*port++ = '\0'; /* erase ':' */// 格式化rtp访问链地址描述,并保存在[psz_chain]if (asprintf (&psz_chain,"rtp{mux=\"%s\",proto=\"%s\",dst=\"%s%s%s\"}",mrl.psz_way, mrl.psz_access, mrl.psz_name,port ? "\",port=\"" : "", port ? port : "") == -1)psz_chain = NULL;}else{// 若不是RTP访问方式,则转换为标准sout输出链地址描述/* Convert the URL to a basic standard sout chain */if (asprintf (&psz_chain,"standard{mux=\"%s\",access=\"%s\",dst=\"%s\"}",mrl.psz_way, mrl.psz_access, mrl.psz_name) == -1)psz_chain = NULL;}// duplicate模式开启即同时流媒体输出和本地播放/* Duplicate and wrap if sout-display is on */if (psz_chain && b_sout_display){// 因此此处将URL链描述符格式化为两个dst内容,并标记duplicate模式char *tmp;if (asprintf (&tmp, "duplicate{dst=display,dst=%s}", psz_chain) == -1)tmp = NULL;free (psz_chain);psz_chain = tmp;}mrl_Clean( &mrl );return psz_chain;
}// 【vlc/src/stream_output/stream_output.c】
// 根据注释:创建一个完整的"stream_out"模块链描述
/* Creates a complete "stream_out" modules chain**  chain format: module1{option=*:option=*}[:module2{option=*:...}]**  The modules are created starting from the last one and linked together*  A pointer to the last module created is stored if pp_last isn't NULL, to*  make sure sout_StreamChainDelete doesn't delete modules created in another*  place.**  Returns a pointer to the first module.*/
sout_stream_t *sout_StreamChainNew(sout_instance_t *p_sout, const char *psz_chain,sout_stream_t *p_next, sout_stream_t **pp_last)
{if(!psz_chain || !*psz_chain){if(pp_last) *pp_last = NULL;return p_next;}char *psz_parser = strdup(psz_chain);if(!psz_parser)return NULL;vlc_array_t cfg, name;vlc_array_init(&cfg);vlc_array_init(&name);// 循环解析vlc串流链描述【例如::sout=#transcode{vcodec=h264,vb=800,scale=自动,// acodec=mp4a,ab=128,channels=2,samplerate=8000,scodec=none}:display :sout-all :sout-keep】/* parse chain */while(psz_parser){config_chain_t *p_cfg;char *psz_name;// 解析串流链K-V值char *psz_rest_chain = config_ChainCreate( &psz_name, &p_cfg, psz_parser );free( psz_parser );psz_parser = psz_rest_chain;// 数组中保存解析出的配置值vlc_array_append_or_abort(&cfg, p_cfg);vlc_array_append_or_abort(&name, psz_name);}size_t i = vlc_array_count(&name);vlc_array_t module;vlc_array_init(&module);while(i--){// 创建每个串流模块,传入串流模块名字和配置信息等// 见下面的分析p_next = sout_StreamNew( p_sout, vlc_array_item_at_index(&name, i),vlc_array_item_at_index(&cfg, i), p_next);if(!p_next)goto error;// 将最后一个串流模块赋值if(i == vlc_array_count(&name) - 1 && pp_last)*pp_last = p_next;   /* last module created in the chain */// 将每次创建的流模块保存在数组中,该数组只用于在error时便于删除的vlc_array_append_or_abort(&module, p_next);}vlc_array_clear(&name);vlc_array_clear(&cfg);vlc_array_clear(&module);return p_next;error:
// ... 省略部分代码return NULL;
}// 【vlc/src/stream_output/stream_output.c】
/* Create a "stream_out" module, which may forward its ES to p_next module */
/** XXX name and p_cfg are used (-> do NOT free them)*/
static sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_name,config_chain_t *p_cfg, sout_stream_t *p_next)
{sout_stream_t *p_stream;assert(psz_name);p_stream = vlc_custom_create( p_sout, sizeof( *p_stream ), "stream out" );if( !p_stream )return NULL;// 初始化保存信息p_stream->p_sout   = p_sout;p_stream->psz_name = psz_name;p_stream->p_cfg    = p_cfg;p_stream->p_next   = p_next;p_stream->pf_flush = NULL;p_stream->pf_control = NULL;p_stream->pace_nocontrol = false;p_stream->p_sys = NULL;msg_Dbg( p_sout, "stream=`%s'", p_stream->psz_name );// 加载对应的串流功能组件【"sout stream"】// 见第2小节分析p_stream->p_module =module_need( p_stream, "sout stream", p_stream->psz_name, true );if( !p_stream->p_module ){/* those must be freed by the caller if creation failed */p_stream->psz_name = NULL;p_stream->p_cfg = NULL;sout_StreamDelete( p_stream );return NULL;}p_sout->i_out_pace_nocontrol += p_stream->pace_nocontrol;return p_stream;
}

自此sout对象初始化创建过程已分析完毕。其调用处理流程请查看第5章节中sout相关分析流程【如第2.3.1小节分析】,并且通过上面的分析可知,sout输出模块组件是可能有多个存在即不同传输方式

2、sout流媒体输出组件模块的加载:
‘通过全局搜索串流功能组件【“sout stream”】如下,

// "Automatically add/delete input streams"
autodel.c (vlc\modules\stream_out) line 48 :     set_capability( "sout stream", 50 )// 流媒体桥接方案? TODO
bridge.c (vlc\modules\stream_out) line 96 :     set_capability( "sout stream", 50 )
bridge.c (vlc\modules\stream_out) line 109 :     set_capability( "sout stream", 50 )// google的投屏技术? TODO
cast.cpp (vlc\modules\stream_out\chromecast) line 241 :     set_capability("sout stream", 0)
cast.cpp (vlc\modules\stream_out\chromecast) line 266 :         set_capability("sout stream", 0)// 音频指纹处理模块,类似可开发歌曲识别功能
chromaprint.c (vlc\modules\stream_out) line 62 :     set_capability( "sout stream", 0 )// 周期性循环输出流
cycle.c (vlc\modules\stream_out) line 328 :     set_capability("sout stream", 0)// 为每个基本码流指定一个延迟时间处理
delay.c (vlc\modules\stream_out) line 56 :     set_capability( "sout stream", 50 )// description stream output module (gathers ES info)
description.c (vlc\modules\stream_out) line 55 :     set_capability( "sout stream", 50 )// 播放输出流【传入解码器进行解码后播放】
display.c (vlc\modules\stream_out) line 55 :     set_capability( "sout stream", 50 )// 假实现即空实现
dummy.c (vlc\modules\stream_out) line 51 :     set_capability( "sout stream", 50 )// vlc中比较重要的功能实现:
// duplicate【复制】输出流推流模式即服务端推流的同时进行播放
duplicate.c (vlc\modules\stream_out) line 45 :     set_capability( "sout stream", 50 )// 基本流推流组件【"Elementary stream output"】 实现单一码流的推流方式
// 例如,annexb就是h264裸码流Elementary Stream的格式
// ES流通常是指编码器的音频或视频输出, 且只能包含一种类型的数据: 如音频数据或视频数据或字幕等.
// PES用于定义如何在TS和PS流中以包的形式携带ES流.
es.c (vlc\modules\stream_out) line 81 :     set_capability( "sout stream", 50 )// 收集流输出模块组件? 后续看情况分析 TODO
gather.c (vlc\modules\stream_out) line 46 :     set_capability( "sout stream", 50 )// 马赛克效果处理流
mosaic_bridge.c (vlc\modules\stream_out) line 141 :     set_capability( "sout stream", 0 )// 录制输出流模块
record.c (vlc\modules\stream_out) line 58 :     set_capability( "sout stream", 0 )// RTP【RTSP】传输方式
rtp.c (vlc\modules\stream_out) line 189 :     set_capability( "sout stream", 0 )setid.c (vlc\modules\stream_out) line 64 :     set_capability( "sout stream", 50 )
setid.c (vlc\modules\stream_out) line 77 :     set_capability( "sout stream", 50 )// 流输出到内存缓冲区中处理 【"Stream output to memory buffer"】
smem.c (vlc\modules\stream_out) line 98 :     set_capability( "sout stream", 0 )// 标准流输出模块 【"Standard stream output"】,支持【"std", "file", "http", "udp", "srt"】
standard.c (vlc\modules\stream_out) line 94 :     set_capability( "sout stream", 50 )// 将流相关统计状态值输出到文件中而非通过标准输出模块【打印】
// 【"Writes stats to file instead of stdout"】
stats.c (vlc\modules\stream_out) line 53 :     set_capability( "sout stream", 0 )// 转码输出流,该模块在流输出需要进行转码时加载的
transcode.c (vlc\modules\stream_out\transcode) line 148 :     set_capability( "sout stream", 50 )

如上,主要分析的模块组件有:

// vlc中比较重要的功能实现:
// duplicate【复制】输出流推流模式即服务端推流的同时进行播放
duplicate.c (vlc\modules\stream_out) line 45 :     set_capability( "sout stream", 50 )// 基本流推流组件【"Elementary stream output"】 实现单一码流的推流方式
// 例如,annexb就是h264裸码流Elementary Stream的格式
// ES流通常是指编码器的音频或视频输出, 且只能包含一种类型的数据: 如音频数据或视频数据或字幕等.
// PES用于定义如何在TS和PS流中以包的形式携带ES流.
es.c (vlc\modules\stream_out) line 81 :     set_capability( "sout stream", 50 )// RTP【RTSP】传输方式
rtp.c (vlc\modules\stream_out) line 189 :     set_capability( "sout stream", 0 )// 转码输出流,该模块在流输出需要进行转码时加载的
transcode.c (vlc\modules\stream_out\transcode) line 148 :     set_capability( "sout stream", 50 )

本章分析RTP推流方式【vlc中live555没有用来推流,只用来拉流,推流vlc自己实现的】,其它实现组件后续接着分析

// RTP【RTSP】传输方式
rtp.c (vlc\modules\stream_out) line 189 :     set_capability( "sout stream", 0 )

RTP推流模块组件声明:【vlc/modules/stream_out/rtp.c】

#define SOUT_CFG_PREFIX "sout-rtp-"
#define MAX_EMPTY_BLOCKS 200vlc_module_begin ()set_shortname( N_("RTP"))set_description( N_("RTP stream output") )set_capability( "sout stream", 0 )// 【vod: video on demand 视频点播技术】add_shortcut( "rtp", "vod" )set_category( CAT_SOUT )set_subcategory( SUBCAT_SOUT_STREAM )add_string( SOUT_CFG_PREFIX "dst", "", DEST_TEXT,DEST_LONGTEXT, true )add_string( SOUT_CFG_PREFIX "sdp", "", SDP_TEXT,SDP_LONGTEXT, true )add_string( SOUT_CFG_PREFIX "mux", "", MUX_TEXT,MUX_LONGTEXT, true )add_bool( SOUT_CFG_PREFIX "sap", false, SAP_TEXT, SAP_LONGTEXT,true )add_string( SOUT_CFG_PREFIX "name", "", NAME_TEXT,NAME_LONGTEXT, true )add_string( SOUT_CFG_PREFIX "cat", "", CAT_TEXT, CAT_LONGTEXT, true )add_string( SOUT_CFG_PREFIX "description", "", DESC_TEXT,DESC_LONGTEXT, true )add_string( SOUT_CFG_PREFIX "url", "", URL_TEXT,URL_LONGTEXT, true )add_string( SOUT_CFG_PREFIX "email", "", EMAIL_TEXT,EMAIL_LONGTEXT, true )add_obsolete_string( SOUT_CFG_PREFIX "phone" ) /* since 3.0.0 */add_string( SOUT_CFG_PREFIX "proto", "udp", PROTO_TEXT,PROTO_LONGTEXT, false )change_string_list( ppsz_protos, ppsz_protocols )add_integer( SOUT_CFG_PREFIX "port", 5004, PORT_TEXT,PORT_LONGTEXT, true )add_integer( SOUT_CFG_PREFIX "port-audio", 0, PORT_AUDIO_TEXT,PORT_AUDIO_LONGTEXT, true )add_integer( SOUT_CFG_PREFIX "port-video", 0, PORT_VIDEO_TEXT,PORT_VIDEO_LONGTEXT, true )add_integer( SOUT_CFG_PREFIX "ttl", -1, TTL_TEXT,TTL_LONGTEXT, true )add_bool( SOUT_CFG_PREFIX "rtcp-mux", false,RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false )add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000,CACHING_TEXT, CACHING_LONGTEXT, true )// RTP (Real-time Transport Protocol) and SRTP (Secure RTP) 安全RTP
#ifdef HAVE_SRTPadd_string( SOUT_CFG_PREFIX "key", "",SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false )add_string( SOUT_CFG_PREFIX "salt", "",SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false )
#endifadd_bool( SOUT_CFG_PREFIX "mp4a-latm", false, RFC3016_TEXT,RFC3016_LONGTEXT, false )// 见下面2.1小节分析set_callbacks( Open, Close )// 加载RTSP子模块【服务端】add_submodule ()set_shortname( N_("RTSP VoD" ) )set_description( N_("RTSP VoD server") )set_category( CAT_SOUT )set_subcategory( SUBCAT_SOUT_VOD )set_capability( "vod server", 10 )// 见第十五章节【Part 5】rtsp VoD server服务器端实现分析set_callbacks( OpenVoD, CloseVoD )add_shortcut( "rtsp" )// 默认超时60s 【通过后续分析可知单位为秒】add_integer( "rtsp-timeout", 60, RTSP_TIMEOUT_TEXT,RTSP_TIMEOUT_LONGTEXT, true )// 用户和密码add_string( "sout-rtsp-user", "",RTSP_USER_TEXT, RTSP_USER_LONGTEXT, true )add_password( "sout-rtsp-pwd", "",RTSP_PASS_TEXT, RTSP_PASS_LONGTEXT, true )vlc_module_end ()

2.1、RTP模块组件初始化入口方法Open实现:

// 【vlc/modules/stream_output/rtp.c】
static int Open( vlc_object_t *p_this )
{sout_stream_t       *p_stream = (sout_stream_t*)p_this;sout_stream_sys_t   *p_sys = NULL;char                *psz;bool          b_rtsp = false;// 解析sout流对象配置信息,并创建对应的变量保存config_ChainParse( p_stream, SOUT_CFG_PREFIX,ppsz_sout_options, p_stream->p_cfg );p_sys = malloc( sizeof( sout_stream_sys_t ) );if( p_sys == NULL )return VLC_ENOMEM;// 获取上面的流媒体配置信息p_sys->psz_destination = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "dst" );p_sys->i_port       = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port" );p_sys->i_port_audio = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-audio" );p_sys->i_port_video = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-video" );p_sys->rtcp_mux     = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );if( p_sys->i_port_audio && p_sys->i_port_video == p_sys->i_port_audio ){// 错误原因:音频和视频的RTP流端口必须不一样msg_Err( p_stream, "audio and video RTP port must be distinct" );free( p_sys->psz_destination );free( p_sys );return VLC_EGENERIC;}for( config_chain_t *p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next ){// 检查配置中是否有启动RTSP传输协议方式if( !strcmp( p_cfg->psz_name, "sdp" )&& ( p_cfg->psz_value != NULL )&& !strncasecmp( p_cfg->psz_value, "rtsp:", 5 ) ){b_rtsp = true;break;}}if( !b_rtsp ){// 若不是RTSP传输方式,则获取sdp描述【session descriptor protocol】psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );if( psz != NULL ){if( !strncasecmp( psz, "rtsp:", 5 ) )// 此处sdp描述也是用rtspb_rtsp = true;free( psz );}}// 下面接着是传输层协议的选择 【vlc中rtp推流默认使用UDP网络传输方式】/* Transport protocol */p_sys->proto = IPPROTO_UDP;// 获取配置协议psz = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"proto");if ((psz == NULL) || !strcasecmp (psz, "udp"))// 注意:vlc中rtp推流默认使用UDP网络传输方式(void)0; /* default */elseif (!strcasecmp (psz, "dccp")){// 数据包拥塞控制协议(DCCP)是一个提供双向单播拥塞控制连接的不可靠数据包传输协议,// 它适合传输相当大的数据量的应用,并且能在时间线和可靠性上权衡。// DCCP使用一个缓存来取代TCP的探测帧,这样减少了网络开销p_sys->proto = IPPROTO_DCCP;p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */}
#if 0elseif (!strcasecmp (psz, "sctp")) // 默认不开启{// 传输层协议,SCTP兼有TCP及UDP两者的特点,但有区别p_sys->proto = IPPROTO_TCP;p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */}
#endif
#if 0elseif (!strcasecmp (psz, "tcp")) // 默认不开启{p_sys->proto = IPPROTO_TCP;p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */}
#endifelseif (!strcasecmp (psz, "udplite") || !strcasecmp (psz, "udp-lite"))// 轻量级用户数据包协议(UDP-Lite) p_sys->proto = IPPROTO_UDPLITE;elsemsg_Warn (p_this, "unknown or unsupported transport protocol \"%s\"",psz);free (psz);var_Create (p_this, "dccp-service", VLC_VAR_STRING);// 由上的网络传输协议处理分析,可知vlc默认不支持TCP传输协议p_sys->p_vod_media = NULL;p_sys->psz_vod_session = NULL;if (! strcmp(p_stream->psz_name, "vod")){// 若name等于“vod”的处理/* The VLM stops all instances before deleting a media, so this* reference will remain valid during the lifetime of the rtp* stream output. */p_sys->p_vod_media = var_InheritAddress(p_stream, "vod-media");if (p_sys->p_vod_media != NULL){p_sys->psz_vod_session = var_InheritString(p_stream, "vod-session");if (p_sys->psz_vod_session == NULL){msg_Err(p_stream, "missing VoD session");free(p_sys);return VLC_EGENERIC;}const char *mux = vod_get_mux(p_sys->p_vod_media);var_SetString(p_stream, SOUT_CFG_PREFIX "mux", mux);}}if( p_sys->psz_destination == NULL && !b_rtsp&& p_sys->p_vod_media == NULL ){msg_Err( p_stream, "missing destination and not in RTSP mode" );free( p_sys );return VLC_EGENERIC;}// 这是设置的Time to Live 生存时间值?int i_ttl = var_GetInteger( p_stream, SOUT_CFG_PREFIX "ttl" );if( i_ttl != -1 ){var_Create( p_stream, "ttl", VLC_VAR_INTEGER );var_SetInteger( p_stream, "ttl", i_ttl );}p_sys->b_latm = var_GetBool( p_stream, SOUT_CFG_PREFIX "mp4a-latm" );// NPT时间(Normal Play Time) 正常播放时间。/* NPT=0 time will be determined when we packetize the first packet* (of any ES). But we want to be able to report rtptime in RTSP* without waiting (and already did in the VoD case). So until then,* we use an arbitrary reference PTS for timestamp computations, and* then actual PTS will catch up using offsets. */p_sys->i_npt_zero = VLC_TS_INVALID;// 初始化一个任意的开始PTS时间戳值,默认使用【p_vod_media】指针值,长度48位bitp_sys->i_pts_zero = rtp_init_ts(p_sys->p_vod_media,p_sys->psz_vod_session);p_sys->i_es = 0;p_sys->es   = NULL;p_sys->rtsp = NULL;p_sys->psz_sdp = NULL;p_sys->b_export_sap = false;p_sys->p_session = NULL;p_sys->psz_sdp_file = NULL;p_sys->p_httpd_host = NULL;p_sys->p_httpd_file = NULL;p_stream->p_sys     = p_sys;vlc_mutex_init( &p_sys->lock_sdp );vlc_mutex_init( &p_sys->lock_ts );vlc_mutex_init( &p_sys->lock_es );// 获取配置的流数据复用类型【音视频流封装格式】:只支持PS或TS// 若为空则表示不需要复用器功能psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );if( psz != NULL ){// 需要加载流复用器模块功能/* Check muxer type */if( strncasecmp( psz, "ps", 2 )&& strncasecmp( psz, "mpeg1", 5 )&& strncasecmp( psz, "ts", 2 ) ){msg_Err( p_stream, "unsupported muxer type for RTP (only TS/PS)" );free( psz );vlc_mutex_destroy( &p_sys->lock_sdp );vlc_mutex_destroy( &p_sys->lock_ts );vlc_mutex_destroy( &p_sys->lock_es );free( p_sys->psz_vod_session );free( p_sys->psz_destination );free( p_sys );return VLC_EGENERIC;}// 输出访问对象p_sys->p_grab = GrabberCreate( p_stream );// 创建初始化流媒体复用器模块【文件格式】// 见2.2小节分析p_sys->p_mux = sout_MuxNew( p_stream->p_sout, psz, p_sys->p_grab );free( psz );if( p_sys->p_mux == NULL ){msg_Err( p_stream, "cannot create muxer" );sout_AccessOutDelete( p_sys->p_grab );vlc_mutex_destroy( &p_sys->lock_sdp );vlc_mutex_destroy( &p_sys->lock_ts );vlc_mutex_destroy( &p_sys->lock_es );free( p_sys->psz_vod_session );free( p_sys->psz_destination );free( p_sys );return VLC_EGENERIC;}p_sys->packet = NULL;// 添加输出流对象对应功能方法指针// 三功能分析见十五章节【Part 4】部分分析 TODOp_stream->pf_add  = MuxAdd;p_stream->pf_del  = MuxDel;p_stream->pf_send = MuxSend;}else{// 不需要加载流复用器模块功能p_sys->p_mux    = NULL;p_sys->p_grab   = NULL;// 添加输出流对象对应功能方法指针// 三功能分析见十五章节【Part 3】部分分析 TODOp_stream->pf_add    = Add;p_stream->pf_del    = Del;p_stream->pf_send   = Send;}// 默认不控制输出流速度p_stream->pace_nocontrol = true;// SAP(Session Announcement Protocol,会话通告协议)、SDP(Session Description Protocol,会话描述协议)if( var_GetBool( p_stream, SOUT_CFG_PREFIX"sap" ) )// 使用SAP处理SDP协议信息// 见2.3小节分析SDPHandleUrl( p_stream, "sap://" );// SDP会话描述协议内容psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );if( psz != NULL ){config_chain_t *p_cfg;// 处理SDP信息// 见2.3小节分析SDPHandleUrl( p_stream, psz );// 根据网络传输协议配置来处理SDPfor( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next ){if( !strcmp( p_cfg->psz_name, "sdp" ) ){if( p_cfg->psz_value == NULL || *p_cfg->psz_value == '\0' )continue;/* needed both :sout-rtp-sdp= and rtp{sdp=} can be used */if( !strcmp( p_cfg->psz_value, psz ) )continue;// 见2.3小节分析SDPHandleUrl( p_stream, p_cfg->psz_value );}}free( psz );}if( p_sys->p_mux != NULL ){// 媒体流复用器不为空时// 见第十五章节【Part 3】章节分析sout_stream_id_sys_t *id = Add( p_stream, NULL );if( id == NULL ){Close( p_this );return VLC_EGENERIC;}}return VLC_SUCCESS;
}

2.2、sout_MuxNew实现分析:

// 【vlc/src/stream_output/stream_output.c】
sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, const char *psz_mux,sout_access_out_t *p_access )
{sout_mux_t *p_mux;char       *psz_next;p_mux = vlc_custom_create( p_sout, sizeof( *p_mux ), "mux" );if( p_mux == NULL )return NULL;p_mux->p_sout = p_sout;// 解析音视频流包括字幕流的复用封装格式【串流链K-V值】psz_next = config_ChainCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );free( psz_next );p_mux->p_access     = p_access;p_mux->pf_control   = NULL;p_mux->pf_addstream = NULL;p_mux->pf_delstream = NULL;p_mux->pf_mux       = NULL;p_mux->i_nb_inputs  = 0;p_mux->pp_inputs    = NULL;p_mux->p_sys        = NULL;p_mux->p_module     = NULL;p_mux->b_add_stream_any_time = false;// 默认需要等待添加流,而不是任意时间的添加p_mux->b_waiting_stream = true;p_mux->i_add_stream_start = -1;// 复用器【"sout mux"】模块组件加载// 见第十六章节流媒体复用分析p_mux->p_module =module_need( p_mux, "sout mux", p_mux->psz_mux, true );if( p_mux->p_module == NULL ){FREENULL( p_mux->psz_mux );vlc_object_release( p_mux );return NULL;}// 分析复用器能力/* *** probe mux capacity *** */if( p_mux->pf_control ){int b_answer = false;// 请求【p_mux->pf_control】复用器该方法if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,&b_answer ) ){b_answer = false;}if( b_answer ){// 为true表示,当前流媒体复用器支持任意时间添加新的流数据进行文件格式复用操作,不需要等待msg_Dbg( p_sout, "muxer support adding stream at any time" );p_mux->b_add_stream_any_time = true;p_mux->b_waiting_stream = false;// 若需要控制输出流速度则最好在开始复用操作之前进行wait操作,以便获得更好的输出流或文件/* If we control the output pace then it's better to wait before* starting muxing (generates better streams/files). */if( !p_sout->i_out_pace_nocontrol ){// 不控制输出流速度时,默认在开始流复用操作之前进行wait操作b_answer = true;}else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT,&b_answer ) ){// 复用器请求失败则默认falseb_answer = false;}if( b_answer ){// 流复用器【推荐】等待所有的ES(Elementary Stream)流数据后才开始复用操作msg_Dbg( p_sout, "muxer prefers to wait for all ES before ""starting to mux" );p_mux->b_waiting_stream = true;}}}return p_mux;
}

2.3、SDPHandleUrl实现分析:【选择应用层协议来发送SDP信息】

// 【vlc/modules/stream_output/rtp.c】
static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
{sout_stream_sys_t *p_sys = p_stream->p_sys;vlc_url_t url;// 解析URLvlc_UrlParse( &url, psz_url );if( url.psz_protocol && !strcasecmp( url.psz_protocol, "http" ) ){// http网络协议时if( p_sys->p_httpd_file ){// 只允许初始化一次msg_Err( p_stream, "you can use sdp=http:// only once" );goto out;}// 初始化http功能【流媒体HTTP网络传输】// 该方式目前暂不考虑分析 TODOif( HttpSetup( p_stream, &url ) ){// 失败,无法使用HTTP网络传输方式进行SDP会话描述协议请求msg_Err( p_stream, "cannot export SDP as HTTP" );}}else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "rtsp" ) ){// RTSP流媒体传输协议时if( p_sys->rtsp != NULL ){// 只能初始化一次哦msg_Err( p_stream, "you can use sdp=rtsp:// only once" );goto out;}if( url.psz_host != NULL && *url.psz_host ){// 注译:RTSP主机地址在多主机地址配置中可能会被忽略,请自行承担使用风险。msg_Warn( p_stream, "\"%s\" RTSP host might be ignored in ""multiple-host configurations, use at your own risks.",url.psz_host );// 打印信息          msg_Info( p_stream, "Consider passing --rtsp-host=IP on the ""command line instead." );// 创建rtsp主机地址变量并保存值var_Create( p_stream, "rtsp-host", VLC_VAR_STRING );var_SetString( p_stream, "rtsp-host", url.psz_host );}if( url.i_port != 0 ){// 端口号不为空时,创建对应变量并保存值/* msg_Info( p_stream, "Consider passing --rtsp-port=%u on ""the command line instead.", url.i_port ); */var_Create( p_stream, "rtsp-port", VLC_VAR_INTEGER );var_SetInteger( p_stream, "rtsp-port", url.i_port );}// rtsp模块功能初始化// 见第十七章节RTSP模块功能分析 TODOp_sys->rtsp = RtspSetup( VLC_OBJECT(p_stream), NULL, url.psz_path );if( p_sys->rtsp == NULL )// 失败,无法使用RTSP网络传输协议进行SDP会话描述协议请求msg_Err( p_stream, "cannot export SDP as RTSP" );}else if( ( url.psz_protocol && !strcasecmp( url.psz_protocol, "sap" ) ) ||( url.psz_host && !strcasecmp( url.psz_host, "sap" ) ) ){// 使用会话通告协议传输SDP会话描述协议内容p_sys->b_export_sap = true;// 该方式目前不考虑分析 TODOSapSetup( p_stream );}else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "file" ) ){// 串流方式:file文件网络传输方式,即将流写入目标文件中// 该方式目前不考虑分析 TODOif( p_sys->psz_sdp_file != NULL ){msg_Err( p_stream, "you can use sdp=file:// only once" );goto out;}p_sys->psz_sdp_file = vlc_uri2path( psz_url );if( p_sys->psz_sdp_file == NULL )goto out;FileSetup( p_stream );}else{// 不支持的未知网络传输协议错误msg_Warn( p_stream, "unknown protocol for SDP (%s)",url.psz_protocol );}out:vlc_UrlClean( &url );
}

本章节到此结束,其他内容请往后章节查看

【十五】【vlc-android】vlc-sout流媒体输出端源码实现分析【Part 1】相关推荐

  1. 【十五】【vlc-android】vlc-sout流媒体输出端源码实现分析【Part 3】【02】

    此章节分析承接上一章分析: [十五][vlc-android]vlc-sout流媒体输出端源码实现分析[Part 3][01] 1.1.2.1.net_ListenSingle实现分析:[本地IP地址 ...

  2. 【十五】【vlc-android】vlc-sout流媒体输出端源码实现分析【Part 2】【03】

    此章节分析承接上一章分析: [十五][vlc-android]vlc-sout流媒体输出端源码实现分析[Part 2][02] 10.1.5.bs_read_ue实现分析: // [vlc/inclu ...

  3. Android 音视频深入 十五 FFmpeg 推流mp4文件(附源码下载)

    源码地址 https://github.com/979451341/Rtmp 1.配置RTMP服务器 这个我不多说贴两个博客分别是在mac和windows环境上的,大家跟着弄 MAC搭建RTMP服务器 ...

  4. Android水面落叶动态壁纸源码及分析 附下载地址

    Android自带的水面落叶动态壁纸效果,尝试使用plasma等jni来实现,最终效果仍然不如renderScript实现的好,因为renderScript相关学习资料比较少,不再重头编写rs脚本来实 ...

  5. Android ---- Ijkplayer阅读native层源码之IjkMediaPlayer_prepareAsync(五)

    整章目录:Android------- IjkPlayer 源码学习目录 本篇会有很多源代码,请注意阅读每行代码上面的注释. 本篇介绍的主要内容为上图红框圈起部分: IjkMediaPlayer_pr ...

  6. Android技术栈(五)HashMap(包括红黑树)与ArrayMap源码解析

    1 总览 本文会对 Android 中常用HashMap(有红黑树)和ArrayMap进行源码解析,其中 HashMap 源码来自 Android Framework API 28 (JDK=1.8) ...

  7. 【十四】【vlc-android】aout音频输出模块源码实现分析【Part 2】

    该章节承接上一章节内容继续分析 上一章节:[十四][vlc-android]aout音频输出模块源码实现分析[Part 1] 3.Stop实现分析:[停止AudioTrack线程等相关操作] // [ ...

  8. 【十四】【vlc-android】aout音频输出模块源码实现分析【Part 1】

    接着第九章节分析aout输出组件模块的加载和实现分析. 有第九章节第2小节中可知,加载的模块名为["audio output"] 通过全局搜索有如下android端支持组件模块: ...

  9. Android --- IjkPlayer 阅读native层源码之解码成功后的音频数据如何发送回Android播放(九)

    整章目录:Android------- IjkPlayer 源码学习目录 本篇会有很多源代码,请注意阅读每行代码上面的注释. 本篇介绍的主要内容为上图红框圈起部分: 在前面介绍了如何将一个AvPack ...

最新文章

  1. 语音标注自动音段对齐工具SPPAS使用笔记
  2. 限制会话id服务端不共享_会话控制 - able-woman - 博客园
  3. Window CMD快捷键
  4. html5如何划分区域,10.4 51单片机 RAM 区域的划分
  5. 占空比50%的奇数分频
  6. Sentinel(五)之流量控制
  7. 陕西2021高考成绩在哪查询,2021陕西高考成绩查询入口
  8. 线程名称的获取与修改
  9. 制作和unity调用动态链接库dll文件
  10. 开课吧:大数据时代,数据分析的特点是什么?
  11. caffe 中的超参
  12. arcgis desktop 地理编码服务发布
  13. 卸载oracle11g全部,完全卸载oracle11g步骤:
  14. Spring boot 项目中dcm文件转jpg文件
  15. 怎么用计算机表示素数,在线质数(素数)计算器
  16. XP教育网用户免费上网
  17. python实现归结演绎推理_归结演绎推理.ppt
  18. 《论文阅读》Joint Demosaicing and Denoising with Self Guidance
  19. 世界首部使用USB-C接口iPhone面世
  20. svc预测概率_机器学习朴素贝叶斯 SVC对新闻文本进行分类

热门文章

  1. 以太网交换机可以家用吗_工业交换机和家用交换机的区别有哪些
  2. html5游戏狗,保卫萝卜2地下庄园D5攻略 极限清理太阳
  3. [转帖]浅析视频监控芯片
  4. synchronized学习
  5. vue2 在element-ui的rate组件中,使用iconfont的图标
  6. 工作成绩和关系的理解
  7. 遍历Map集合的键值对
  8. 深度学习方法实现车道线分割之二(自动驾驶车道线分割)
  9. Ocean/Ocean+: 实时目标跟踪分割算法《Object-aware Anchor-free Tracking》翻译
  10. modelsim/questasim do文件解释以及makefile