从解复用,路过解码器,到输出 sout.
数据从 demux 解复用出来,放到解码器的fifo中,创建 解码器。
进入decoder.c的解码线程,从复用 demux出来的 mp4文件有两个 es流,video, audio,所以会创建两个 decoder,即两个decode线程分析decode线程中将数据 输出到 rtp的流程:
从 demux 解析出来的音频或视频es流,这里选择 rtsp输出(针对该demo的流程分析demo:https://blog.csdn.net/u012459903/article/details/88600045),没有涉及到播放显示。
下图中 部分函数 从rtp中来,关于跳转到rtp中的转折,在后续分析sout的初始化中
有两个单独的解码线程 video audio , 对应有两个 sout 发送数据输出的线程。
rtps的相关部分:
上面图分心了数据从解复用,到解码器里面的一系列历程,最后调用到发送线程,发送线程:
static void* ThreadSend( void *data )
{
sout_stream_id_sys_t *id = data;
...
for (;;)
{
block_t *out = block_FifoGet( id->p_fifo );
...
for( int i = 0; i < id->sinkc; i++ )
{
SendRTCP( id->sinkv[i].rtcp, out )
send( id->sinkv[i].rtp_fd, out->p_buffer, len, 0 )
}
}
...
}
看到这里的send, 用到 sout_stream_id_sys_t *id 中的 sinkv,进行数据 rtcp 和rtp的发送,这个 sinkv,即rtsp"过滤器"。逆向查找,可以找到他的创建过程:
stream_out/rtp.c 该模块的加载 ,在input线程启动时候调用 init()时,初始化InitSout( p_input )// 流输出 关于input线程已经在上一篇分析https://blog.csdn.net/u012459903/article/details/88896095
补充一下 这个InitSout的分析
input.c 文件
static int InitSout( input_thread_t * p_input )
{
input_thread_private_t *priv = input_priv(p_input);
...
//这里的 psz = "#rtp{sdp=rtsp://192.168.43.129:10086/stream}"
priv->p_sout = input_resource_RequestSout( priv->p_resource, NULL, psz );
==》 //经过几轮转换,调用到strem_output.c 中的 sout_StreamChainNew();创建一个流的链。 即上面的参数还可以增加,相当于添加多个 流的过滤器, 组成一个链条一样的一串过滤器, 我这里只有一个,所以这个链里面其实只有一个stream 过滤器。
sout_StreamChainNew 内部调用 sout_StreamNew来创建单个的 stream过滤器:
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;
p_stream = vlc_custom_create( p_sout, sizeof( *p_stream ), "stream out" );
...
//重点,这里要加载模块了,此处的 p_stream->psz_name = "rtp"
p_stream->p_module =
module_need( p_stream, "sout stream", p_stream->psz_name, true );
...
}
}
上面是从 input.c 到 rtp.c文件的跳转,和这个rtsp的关系,在rtp模块的 open中
static int Open( vlc_object_t *p_this )
{
...
sout_stream_t *p_stream = (sout_stream_t*)p_this;
...
psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
if( psz != NULL )
{
// 参数 ps = "rtsp://192.168.43.129:10086/stream "
SDPHandleUrl( p_stream, psz );
//
SDPHandleUrl()这个函数,在分析到上面的 rtsp后,调用
p_sys->rtsp = RtspSetup( VLC_OBJECT(p_stream), NULL, url.psz_path );
激活rtsp。
==> RtspHandler()==>
rtp_add_sink()
{ ...
rtp_sink_t sink = { fd, NULL };
sink.rtcp = OpenRTCP( VLC_OBJECT( id->p_stream ), fd, IPPROTO_UDP,
rtcp_mux );
TAB_APPEND(id->sinkc, id->sinkv, sink);
...
}
...
}
...
}
rtp的 open 过程中,根据url,激活了rtsp, 然后再rtsp的控制中 调用 rtp_add_sink() 设置到 sout_stream_id_sys_t *id ->sinkv
这个就是ThreadSedn线程 用到的sinkv. (rtsp 发送这部分需要有客户端连接才会触发)