OBS-RTMP推流

OBS-RTMP推流

1、OBSBasic::StartStreaming()

    //调用函数
    outputHandler->SetupStreaming(service)
    outputHandler->StartStreaming(service)
outputHandler是指SimpleOutput或者AdvancedOutput,这两者都继承自BasicOutputHandler。
具体使用哪一个,由用户在 设置->输出->输出模式中配置。
AdvancedOutput提供了更多的编码配置选项,包括音频和视频。

2、 SetupStreaming(obs_service_t *service)

    //调用函数
    if (!Active())
        SetupOutputs();
    streamOutput =obs_output_create();
    
    //设置streamOutput->video_encoder为h264Streaming
    //也就是为视频的编码器
    
    //h264Streaming目前可以使用的有x264 、nvenc等
    
    obs_output_set_video_encoder(streamOutput, h264Streaming);
    //设置streamOutput->audio_encoders[0]为streamAudioEnc
    obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);
1、配置流输出参数
2、创建输出

2.1 SetupOutputs()

    //为视频编码器h264Streaming设置视频源
    obs_encoder_set_video(h264Streaming, obs_get_video());
    if (h264Recording)
        obs_encoder_set_video(h264Recording, obs_get_video());
    for (size_t i = 0; i < MAX_AUDIO_MIXES; i++)
        obs_encoder_set_audio(aacTrack[i], obs_get_audio());
        
    //为音频编码器h264Streaming设置音频源
    obs_encoder_set_audio(streamAudioEnc, obs_get_audio());
    obs_encoder_set_audio(streamArchiveEnc, obs_get_audio());
    
    //设置编码器的参数例如视频 分辨率、比特率等
    SetupStreaming();

2.2 obs_output_create()

    //调用函数
    info = find_output(id); //从输出插件组中找到与id相同的输出插件
                            //对于rtmp来讲就是rtmp输出插件。
    //创建一个obs_output结构
    //初始化obs_output中的参数,主要是互斥锁、信号处理函数
    //初始化重连参数
    
    //设置视频源
    output->video = obs_get_video();
    //设置音频源
    output->audio = obs_get_audio();
    
    //调用输出插件的create函数。
    //对于rtmp来讲就是rtmp_stream_create(),返回值为struct rtmp_stream *;
    //将返回值赋值给output->context.data
    output->context.data = info->create(output->context.settings,
            output); 

2.2.1 info->create()

对于rtmp输出来讲它调用了rtmp_stream_create()
1、创建rtmp_stream,它是OBS通过rtmp协议进行推流的主要模块;
2、RTMP_Init();
3、对rtmp_stream中的成员变量初始化,主要是互斥锁和条件变量;
4、返回struct rtmp_stream *;

3、StartStreaming()

通过SetupStreaming进行一系列输出配置过后,调用StartStreaming启动推流。
    //设置重连参数
    //延迟参数
    //绑定ip
    //调用函数
    obs_output_start(streamOutput);

3.1 obs_output_start()

    //output->info 指向一个结构体,结构体内存放的是输出插件所对应的一组函数和
    //插件所对应的flags,对于rtmp来讲就是rtmp输出插件
    //
    bool has_service = (output->info.flags & OBS_OUTPUT_SERVICE) != 0;
    // rtmp输出插件设置了OBS_OUTPUT_SERVICE,
    // service必须通过 obs_output_set_service() 函数分配,它是推流的必要配置。这个service 通常是指twitch、YouTube等推流服务器
    // 如果设置了initialize函数,则调用对应的初始化函数。
    if (has_service && !obs_service_initialize(output->service, output))
    {
        return false;
    }

    // encoded是指输出是已编码数据
    // rtmp输出插件设置了OBS_OUTPUT_ENCODED
    bool encoded = (output->info.flags & OBS_OUTPUT_ENCODED) != 0;
    // delay_sec 串流延迟时间。由用户在配置里设置串流延迟。
    if (encoded && output->delay_sec) {
        obs_output_delay_start(output);
    } else {
        obs_output_actual_start(output)
    }

3.1.2 obs_output_delay_start()

    //这里主要是设置一个延时处理的事件
	struct delay_data dd = {
		.msg = DELAY_MSG_START,
		.ts = os_gettime_ns(),
	};

    //做一些检查
    
    //将dd 放入到output->delay_data 随后会用到
	circlebuf_push_back(&output->delay_data, &dd, sizeof(dd));

3.1.1 obs_output_actual_start()

    //等待停止操作完成
    os_event_wait(output->stopping_event);
    // output->context.data:在obs_output_create,通过调用info->create创建,
    // 对于rtmp来讲output->context.data是指向struct rtmp_stream 的指针。
    
    //output->info.start 对于rtmp来讲是rtmp_stream_start。
    if (output->context.data)
        success = output->info.start(output->context.data);

3.1.1.1 rtmp_stream_start()

    //开始前进行一系列的判断,是否能够开始
    
    //创建连接线程。
     pthread_create(&stream->connect_thread, NULL, connect_thread,stream) == 0;
    
3.1.1.1.1 connect_thread()
开启新的线程处理耗时的链接过程。
    //该线程中主要调用以下连个函数
    //初始化连接
    if (!init_connect(stream)) {
        obs_output_signal_stop(stream->output, OBS_OUTPUT_BAD_PATH);
        return NULL;
    }
    //尝试连接
    ret = try_connect(stream);
3.1.1.1.1.1 init_connect()
    //如果之前的推流线程还没有退出,则先等待推流线程退出。
    if (stopping(stream)) {
        pthread_join(stream->send_thread, NULL);
    }
    
    //获取配置的推流服务器
    
    //初始化stream的参数,主要是发送数据的统计
    
    //获取推流路径、推流秘钥、用户名、密码
    
    //B帧、P帧丢帧策略
    
    //视频和音频编码器
    obs_encoder_t *venc = obs_output_get_video_encoder(stream->output);
    obs_encoder_t *aenc = obs_output_get_audio_encoder(stream->output, 0);
    
    //获取编码器的配置参数
    obs_data_t *vsettings = obs_encoder_get_settings(venc);
    obs_data_t *asettings = obs_encoder_get_settings(aenc);
    
    //根据编码器的配置获取音频比特率、视频比特率

    
    //绑定的IP
3.1.1.1.1.2 try_connect()
    //在重新连接时,需要重新设置librtmp内部的参数,
    //否则数据发送和接收在另一端不会被正确解析
    RTMP_Reset(&stream->rtmp);
    
    
    //因为我们没有在上面调用RTMP_Init,所以没有其他好地方可以重置它,
    //因为在 RTMP_Close 中这样做会破坏丑陋的 RTMP 身份验证系统
    
    memset(&stream->rtmp.Link, 0, sizeof(stream->rtmp.Link));
    stream->rtmp.last_error_code = 0;
    
    // 设置推拉流的URL。内部调用了RTMP_ParseURL主要用于解析url
    if (!RTMP_SetupURL(&stream->rtmp, stream->path.array))
        return OBS_OUTPUT_BAD_PATH;

    // 设置RTMP_EnableWrite将发送publish命令将命名流发布到服务端。
    RTMP_EnableWrite(&stream->rtmp);
    
    //初始化一个stream id 将流媒体资源(或推流码)绑定到一个stream id上 
    //path 流媒体服务器地址(例如:rtmp://sendtc3.douyu.com/live) 
    //key 流媒体资源(推流码)
    RTMP_AddStream(&stream->rtmp, stream->key.array);
    
    //建立socket 连接,进行rtmp的握手过程,建立rtmp的NetConnection连接。
    if (!RTMP_Connect(&stream->rtmp, NULL)) {
        set_output_error(stream);
        return OBS_OUTPUT_CONNECT_FAILED;
    }

    //创建流/循环读取服务端发送过来的各种消息,比如window ack, set peer bandwidth, set chunk size, _result等
    //直到接收到了play或者publish可以开始的信息。
    if (!RTMP_ConnectStream(&stream->rtmp, 0))
        return OBS_OUTPUT_INVALID_STREAM;
        
    return init_send(stream);
    
3.1.1.1.1.2.1 init_send()
    创建数据发送线程 send_thread()
    ret = pthread_create(&stream->send_thread, NULL, send_thread, stream);
    
    // 发送视频元数据,主要是flv头部信息,包含了音视频的编码。
    if (!send_meta_data(stream)) {
        warn("Disconnected while attempting to send metadata");
        set_output_error(stream);
        return OBS_OUTPUT_DISCONNECTED;
    }
    
    
    obs_output_begin_data_capture(stream->output, 0); 

3.1.1.1.1.2.1.1 send_thread()
    //这里主要是循环等待要发送的数据包,然后发送这个数据包。
    while (os_sem_wait(stream->send_sem) == 0) {
        //判断需不需要停止
        if (stopping(stream) && stream->stop_ts == 0) {
            break;
        }
        //获取数据包
        if (!get_next_packet(stream, &packet))
            continue;
            
        if (!stream->sent_headers) {
            if (!send_headers(stream)) {
                os_atomic_set_bool(&stream->disconnected, true);
                break;
            }
        }
        //
        //发送数据包
        if (send_packet(stream, &packet, false, packet.track_idx) < 0) {
            info("send packet failed, idx: %u", packet.track_idx);
            os_atomic_set_bool(&stream->disconnected, true);
            break;
        }
    }
3.1.1.1.1.2.1.1.1 send_packet()
    //对要发送的数据包进行flv封装。
    if (idx > 0) {
        flv_additional_packet_mux(
            packet, is_header ? 0 : stream->start_dts_offset, &data,
            &size, is_header, idx);
    } else {
        flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset,
                   &data, &size, is_header);
    }
    //通过rtmp发送数据
    ret = RTMP_Write(&stream->rtmp, (char *)data, (int)size, 0);

3.1.1.1.1.2.1.1 obs_output_begin_data_capture(obs_output_t *output, uint32_t flags)
    //这个函数主要是调用hook_data_capture,
    //对于视频来讲,会设置渲染后原始视频帧输出给编码器,编码器对视频编码后输出给音视频同步函数(如果只有音频或视频则输出给默认的回调),
    //音视频同步后输出给obs_output_t 类型的插件,由插件进行推流或者录制。

    hook_data_capture(output, encoded, has_video, has_audio);
    
    //函数:hook_data_capture(output, encoded, has_video, has_audio)
    
    // 数据需要编码
    if (encoded) {

        // 编码回调设置
        
        //如果有音频和视频回调函数就设置为interleave_packets,这里主要是进行音视频的同步。
        //音频和视频只有一个则使用默认的default_encoded_callback函数
        
        encoded_callback = (has_video && has_audio)
                       ? interleave_packets
                       : default_encoded_callback;
        
        //是否设置了延迟,如果设置了延迟则将encoded_callback 设置为process_delay
        //设置output->delay_callback 为encoded_callback
        //这一步对应obs_output_delay_start
        if (output->delay_sec) {
            output->delay_cur_flags = output->delay_flags;
            output->delay_callback = encoded_callback;
            encoded_callback = process_delay;
            os_atomic_set_bool(&output->delay_active, true);
        }
        
        //有音频则启动音频编码
        if (has_audio)
            start_audio_encoders(output, encoded_callback);
            
        //有视频则启动视频编码
        if (has_video)
            obs_encoder_start(output->video_encoder,
                      encoded_callback, output);
    } else {
        // 数据不需要编码,直接发送
        if (has_video)
            start_raw_video(output->video,
                    get_video_conversion(output),
                    default_raw_video_callback, output);
        if (has_audio)
            start_raw_audio(output);
    }

    
    //函数 void obs_encoder_start(obs_encoder_t *encoder,void (*new_packet)(void *param,struct encoder_packet *packet),void *param)
    //参数 param 对应 obs_output_t
    
    //调用函数
    obs_encoder_start_internal(encoder, new_packet, param);
    //函数 void obs_encoder_start_internal(obs_encoder_t *encoder,void (*new_packet)(void *param, struct encoder_packet *packet),void *param))
    
    //设置编码器的 callback的参数,主要是为数据被编码后所需要进行的后续操作,
    // new_packet 对应    interleave_packets或 default_encoded_callback;
    struct encoder_callback cb = {false, new_packet, param};
    
    bool first = (encoder->callbacks.num == 0);
    
    size_t idx = get_callback_idx(encoder, new_packet, param);
    // 将回调函数塞入encoder->callbacks数组中
    if (idx == DARRAY_INVALID)
        da_push_back(encoder->callbacks, &cb);
        
    if (first) {
        .
        .
        .
        add_connection(encoder);
    }   
    //函数 void add_connection(struct obs_encoder *encoder)
    
    if (encoder->info.type == OBS_ENCODER_AUDIO) {
        .
        .
        .
        //设置音频的回调
        audio_output_connect(encoder->media, encoder->mixer_idx,
                     &audio_info, receive_audio, encoder);
    } else {
        .
        .
        .
        //设置视频的回调
        if (gpu_encode_available(encoder)) {
            start_gpu_encode(encoder);
        } else {
            start_raw_video(encoder->media, &info, receive_video,
                    encoder);
        }
    }

    //函数 void start_raw_video(video_t *v, const struct video_scale_info *conversion, void (*callback)(void *param, struct video_data *frame),void *param)
    
    //调用
    
    video_output_connect(v, conversion, callback, param);
    
    //函数 void video_output_connect(video_t *video, const struct video_scale_info *conversion,void (*callback)(void *param, struct video_data *frame), void *param)
    
    
    if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) {
        struct video_input input;
        memset(&input, 0, sizeof(input));
        
        //设置回调函数为obs-encoder.c:receive_video
        input.callback = callback;  // callback: obs-encoder.c:receive_video
        input.param = param;

        //设置参数
        
        //初始化video_input
        success = video_input_init(&input, video);
        if (success) {
            if (video->inputs.num == 0) {
                if (!os_atomic_load_long(&video->gpu_refs)) {
                    reset_frames(video);
                }
                os_atomic_set_bool(&video->raw_active, true);
            }
            
            //将input 塞入video->inputs
            da_push_back(video->inputs, &input);
        }
    }
    
    // 视频编码线程。编码视频帧,编码后将数据输出到文件(录制)或者送给推流
    // 待编码的视频源是由渲染线程提供
    static void *video_thread(void *param)
    {
    
        // 等待渲染线程提供数据 video->update_semaphore == obs->video.video->update_semaphore
        while (os_sem_wait(video->update_semaphore) == 0) {
            
            // video_output_cur_frame
            while (!video->stop && !video_output_cur_frame(video)) {
                os_atomic_inc_long(&video->total_frames);
            }
        }
            
    }
    
    //函数video_output_cur_frame
    
    static inline bool video_output_cur_frame(struct video_output *video)
    {
    
        frame_info = &video->cache[video->first_added]; // 取待编码数据

        for (size_t i = 0; i < video->inputs.num; i++) {
            struct video_input *input = video->inputs.array + i;
            struct video_data frame = frame_info->frame;
            // start_raw_video处设置的回调。receive_video or default_raw_video_callback.
            if (scale_video_output(input, &frame))
                input->callback(input->param, &frame);  
        }
    }
    
    //函数video_output_cur_frame
    
    bool video_output_cur_frame(struct video_output *video)
    {
    
        frame_info = &video->cache[video->first_added]; // 取待编码数据

        for (size_t i = 0; i < video->inputs.num; i++) {
            struct video_input *input = video->inputs.array + i;
            struct video_data frame = frame_info->frame;
            // start_raw_video处设置的回调。receive_video or default_raw_video_callback.
            if (scale_video_output(input, &frame))
                input->callback(input->param, &frame);  
        }
    }
    
    //函数 obs-encoder.c : receive_video(void *param, struct video_data *frame)
    
    static void receive_video(void *param, struct video_data *frame)
    {
        //设置好待编码的数据和参数
        
        // 执行编码enc_frame数据
        if (do_encode(encoder, &enc_frame))
            encoder->cur_pts += encoder->timebase_num;
    }
    
    //函数 obs-encoder.c : bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame)
    
    bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame)
    {
        // 编码后编码后的NAL数据存储在encoder->context.data(obs_x264 *)和pkt中
        // x264 对应为 obs_x264_encode
        success = encoder->info.encode(encoder->context.data, frame, &pkt,
                               &received);  
        send_off_encoder_packet(encoder, success, received, &pkt);
    }
    
    //函数 obs-encoder.c : void send_off_encoder_packet(obs_encoder_t *encoder, bool success,bool received, struct encoder_packet *pkt)
    
    bool send_off_encoder_packet(obs_encoder_t *encoder, bool success,bool received, struct encoder_packet *pkt)
    {
        if (received) {
            //cb 对应为 interleave_packets 或者 default_encoded_callback
            struct encoder_callback *cb;
            cb = encoder->callbacks.array + (i - 1);    
            send_packet(encoder, cb, pkt);
        }
    }
    
    //函数 obs-encoder.c : void send_packet(struct obs_encoder *encoder, struct encoder_callback *cb,struct encoder_packet *packet)
    
    void send_packet(struct obs_encoder *encoder, struct encoder_callback *cb,struct encoder_packet *packet)
    {
        /* include SEI in first video packet */
        if (encoder->info.type == OBS_ENCODER_VIDEO && !cb->sent_first_packet)
            send_first_video_packet(encoder, cb, packet);   // 将视频的SEI信息添加到视频数据中
        else
            //param 对应为struct obs_output *
            cb->new_packet(cb->param, packet);  // 调用interleave_packets 或者 default_encoded_callback 进行视频数据的发送
    }
    
    //函数 obs-output.c : void interleave_packets(void *data, struct encoder_packet *packet)
    
    void interleave_packets(void *data, struct encoder_packet *packet)
    {
        //音频视频的同步
        
        //发送数据
        send_interleaved(output); // 发送数据
    }
    
    // send_interleaved内部主要是调用output->info.encoded_packet(output->context.data,&out);
    //  output->info.encoded_packet 对于rtmp 来讲就是rtmp_stream_data
    

你可能感兴趣的:(#,OBS-Studio,音视频,obs,rtmp)