精华内容
下载资源
问答
  • rtmp推流
    千次阅读
    2022-01-25 10:40:22

    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
        
    
    更多相关内容
  • 桌面共享工具界面优化版(RTMP推流工具、投屏工具、播放工具)EXE,非源码)(水印版,不喜勿下) 功能简介: 1.支持usb摄像头推流。 2.支持桌面推流。 3.支持usb摄像头与桌面叠加切换推流。 4.支持不同分辨率输出。 ...
  • Android手机屏幕RTMP推流工具是一款可以将安卓手机屏幕上的任何内容推送到任意一款rtmp服务器上的工具,不仅可以分享手机屏幕内容,还可以分享手机上正在播放的视频、正在捕获的相机内容、其它APP正在播放的视频等...
  • 安卓rtmp推流工具

    2019-05-10 11:00:41
    安卓app,rtmp推流工具,
  • RTMP 推流器,RTMP(HLS)秒开播放器,跨平台(Win,IOS,Android)开源代码
  • 基于netty 开发 rtmp 推拉服务器代码,可用ffmpeg代码测试
  • 源码参考,欢迎下载
  • rtsp转rtmp推流工具

    2021-07-27 10:52:08
    pyqt实现rtsp拉转推rtmp
  • 今天小编就为大家分享一篇树莓派使用python-librtmp实现rtmp推流h264的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • RTMP推流RTSP视频

    2018-10-14 11:55:18
    RTMP推流
  • Android摄像头RTMP推流

    2018-12-31 22:11:11
    使用FFMPEG的RTMP推流修改而来,可以读取摄像头和音频推流至流媒体服务器,适合做视频直播的新手参考. 使用FFMPEG的RTMP推流修改而来,可以读取摄像头和音频推流至流媒体服务器,适合做视频直播的新手参考.
  • Android RTMP推流实现

    千次下载 热门讨论 2016-12-02 21:37:27
    详见http://blog.csdn.net/huaxun66/article/details/53427771
  • Android直播(rtmp推流拉流),一个是推流module,一个是拉流demo,适配到Android9.0,demo中可以直接使用,很方便集成.推流使用libWsLive,拉流使用vitamio.非常稳定.
  • 海康威视视频,视频 RTSP 转RTMP 。海
  • 基于Yasea-2.6实现的Android RTMP推流APP,下载安装即可推流。 源代码 https://github.com/begeekmyfriend/yasea
  • android 摄像头RTMP推流

    2018-04-28 10:46:20
    android摄像头视频编码 到指定的rtmp服务器。
  • 需要先创建推流的流媒体地址,然后再创建一个对象启动推流,创建过程如下。 RtmpPublishManager* rpm = new RtmpPublishManager("rtmp://192.168.16.88:1935/hls/test0", read_fd[0]); rpm->ffmpeg_init(NULL); rpm-...
  • 网上找了很多videojs 都是各种问题,而且积分特别高,自己整理了一下。测试可用,支持 rtmp 拉流h5自带的视频拉流播放器原生态
  • 基于Nginx+RTMP 配置推拉服务器,以及设置回调功能
  • Window环境下 海康视频RTMP推流方法,使用到的工具。希望能够帮到大家。
  • linux环境rtmp推流

    2018-10-18 14:29:33
    linux环境,基于rtmp推流源代码,源文件可以下载参考学习
  • 世纪葵花为了感谢广大客户多年来...五:支持本地mp4文件转发成网络送给KMS/FMS/Wowza等媒体服务器 六:不限转发频道数量,不限送服务器数量 七:配合KMS媒体系统可以支持PC+机顶盒+手机观看(微信直播)
  • RTMP推流服务器.rar

    2019-06-06 12:42:16
    代码已经完全编译通过,测试通过了。请放心使用。
  • 基于ffmpeg rtmp推流源代码,可集成到系统中。
  • ffmpeg usb摄像头采集 +6818硬件编码 +ffmpeg RTMP H264推流 需要提前移植QT 和FFMPEG ffmpeg移植不需要添加H264编码
  • DesktopSharing项目介绍抓取屏幕和声卡的音视频数据,编码后进行RTSP转发, RTSP推流, RTMP推流。目前情况完成屏幕采集(DXGI)和H.264编码。完成音频采集(WASAPI)和AAC编码。完成RTSP本地转发音视频数据。完成RTSP推流...
  • 使用FFmpeg进行推流开发,ffmpeg -re -i 1.mp4 -vcodec libx264 -acodec aac -f flv rtmp://192.168.9.22:1935/live/home
  • rtmp推拉源码.zip

    2021-07-16 11:59:30
    基于librtmp库的推流与拉流简单示例 运行环境:X86电脑上,需要借助nginx或srs等服务器
  • 本人手机华为P7,640X480,500K,在3G、4G、wifi网络下测试通过。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,317
精华内容 6,926
关键字:

rtmp推流

友情链接: Age of Valor.rar